1.9. 数据访问

.NET 提供多种数据访问技术,从 ADO.NET 到 Entity Framework Core。

1.9.1. ADO.NET

ADO.NET 是 .NET 最底层的数据访问 API,提供直接执行 SQL 的能力。

using System.Data;
using System.Data.SqlClient;

// 连接字符串
string connStr = "Server=.;Database=MyDb;Trusted_Connection=true;TrustServerCertificate=true;";

// 查询
using var conn = new SqlConnection(connStr);
await conn.OpenAsync();

var cmd = new SqlCommand("SELECT Id, Name, Email FROM Users WHERE Id = @id", conn);
cmd.Parameters.AddWithValue("@id", userId);

using var reader = await cmd.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
    int id = reader.GetInt32(0);
    string name = reader.GetString(1);
    string? email = reader.IsDBNull(2) ? null : reader.GetString(2);
}

// 非查询操作(INSERT / UPDATE / DELETE)
var insertCmd = new SqlCommand(@"
    INSERT INTO Users (Name, Email, CreatedAt)
    VALUES (@name, @email, @createdAt)", conn);

insertCmd.Parameters.AddWithValue("@name", "Alice");
insertCmd.Parameters.AddWithValue("@email", "alice@example.com");
insertCmd.Parameters.AddWithValue("@createdAt", DateTime.UtcNow);

int rows = await insertCmd.ExecuteNonQueryAsync();

// 事务
using var tx = await conn.BeginTransactionAsync();
try
{
    cmd.Transaction = (SqlTransaction)tx;
    await cmd.ExecuteNonQueryAsync();
    await tx.CommitAsync();
}
catch
{
    await tx.RollbackAsync();
    throw;
}

警告

始终使用参数化查询,不要拼接 SQL 字符串,防止 SQL 注入。

// 错误!SQL 注入风险
var sql = $"SELECT * FROM Users WHERE Name = '{userInput}'";

// 正确!参数化查询
var cmd = new SqlCommand("SELECT * FROM Users WHERE Name = @name", conn);
cmd.Parameters.AddWithValue("@name", userInput);

1.9.2. Entity Framework Core

EF Core 是 .NET 的首选 ORM,支持多种数据库。

1.9.2.1. DbContext 与实体

// 实体类
public class User
{
    public int Id { get; set; }
    public string Name { get; set; } = "";
    public string? Email { get; set; }
    public DateTime CreatedAt { get; set; }

    // 导航属性
    public ICollection<Order> Orders { get; set; } = new List<Order>();
}

public class Order
{
    public int Id { get; set; }
    public int UserId { get; set; }
    public decimal Amount { get; set; }
    public DateTime OrderDate { get; set; }

    // 导航属性
    public User User { get; set; } = null!;
}

// DbContext
public class AppDbContext : DbContext
{
    public DbSet<User> Users => Set<User>();
    public DbSet<Order> Orders => Set<Order>();

    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        options.UseSqlServer(@"Server=.;Database=MyDb;...");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Fluent API 配置
        modelBuilder.Entity<User>(entity =>
        {
            entity.HasKey(e => e.Id);
            entity.Property(e => e.Name)
                  .IsRequired()
                  .HasMaxLength(100);
            entity.HasIndex(e => e.Email).IsUnique();
        });
    }
}

1.9.2.2. CRUD 操作

// 创建
using var db = new AppDbContext();
var user = new User { Name = "Alice", Email = "alice@example.com" };
db.Users.Add(user);
await db.SaveChangesAsync();     // Id 自动填充

// 查询
var user = await db.Users
    .Include(u => u.Orders)      // Eager Loading
    .FirstOrDefaultAsync(u => u.Id == id);

var users = await db.Users
    .Where(u => u.Name.Contains("A"))
    .OrderBy(u => u.Name)
    .Skip(page * size)
    .Take(size)
    .ToListAsync();

// 更新
var user = await db.Users.FindAsync(id);
if (user != null)
{
    user.Name = "Updated Name";
    await db.SaveChangesAsync();
}

// 删除
var user = await db.Users.FindAsync(id);
if (user != null)
{
    db.Users.Remove(user);
    await db.SaveChangesAsync();
}

// 原生 SQL
var users = await db.Users
    .FromSql($"SELECT * FROM Users WHERE Name LIKE {search}")
    .ToListAsync();

1.9.2.3. Migrations

dotnet ef migrations add InitialCreate
dotnet ef database update

# 生成 SQL 脚本
dotnet ef migrations script

1.9.3. Dapper(轻量级 ORM)

using Dapper;

// 需要安装 Dapper NuGet 包

using var conn = new SqlConnection(connStr);

// 查询
var user = await conn.QueryFirstOrDefaultAsync<User>(
    "SELECT * FROM Users WHERE Id = @Id", new { Id = 1 });

var users = await conn.QueryAsync<User>(
    "SELECT * FROM Users WHERE Name LIKE @Search",
    new { Search = $"%{keyword}%" });

// 执行
int rows = await conn.ExecuteAsync(
    "UPDATE Users SET Name = @Name WHERE Id = @Id",
    new { Name = "Alice", Id = 1 });

// 事务
using var tx = conn.BeginTransaction();
await conn.ExecuteAsync("DELETE FROM Orders WHERE UserId = @Id", new { id }, tx);
await conn.ExecuteAsync("DELETE FROM Users WHERE Id = @Id", new { id }, tx);
tx.Commit();

小技巧

EF Core vs Dapper 选择:

  • EF Core:CRUD 密集、复杂关系映射、快速开发 → 选 EF Core

  • Dapper:性能敏感、纯 SQL 掌控、简单数据映射 → 选 Dapper

  • 兼顾:EF Core 负责复杂业务,Dapper 负责高并发查询

1.9.4. 文件数据库(SQLite)

// 安装 Microsoft.EntityFrameworkCore.Sqlite

public class LocalDbContext : DbContext
{
    public DbSet<ConfigItem> Config => Set<ConfigItem>();

    protected override void OnConfiguring(DbContextOptionsBuilder options)
    {
        options.UseSqlite("Data Source=app.db");
    }
}

// SQLite 适合单机桌面应用、不需要安装数据库服务