Skip to content

数据库提供程序 ​

概述 ​

EF Core 通过**提供程序(Provider)**模式支持多种数据库。每个提供程序负责将 EF Core 的通用操作翻译为特定数据库的 SQL 方言和特性。

支持的数据库 ​

数据库提供程序包成熟度适用场景
SQL ServerMicrosoft.EntityFrameworkCore.SqlServer✅ 官方Windows 企业应用
PostgreSQLNpgsql.EntityFrameworkCore.PostgreSQL✅ 社区Linux/跨平台首选
MySQLPomelo.EntityFrameworkCore.MySql✅ 社区Web 应用广泛使用
SQLiteMicrosoft.EntityFrameworkCore.Sqlite✅ 官方移动/桌面/测试
Cosmos DBMicrosoft.EntityFrameworkCore.Cosmos✅ 官方NoSQL/文档数据库
OracleOracle.EntityFrameworkCore✅ 官方Oracle 生态系统
FirebirdFirebirdSql.EntityFrameworkCore.Firebird⚠️ 社区遗留系统
IBM Db2IBM.EntityFrameworkCore⚠️ 官方大型企业系统

SQL Server ​

安装 ​

bash
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools  # 迁移工具

配置 ​

csharp
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(connectionString, sqlOptions =>
    {
        // 启用重试策略(推荐生产环境)
        sqlOptions.EnableRetryOnFailure(
            maxRetryCount: 3,
            maxRetryDelay: TimeSpan.FromSeconds(10),
            errorNumbersToAdd: null);
        
        // 命令超时
        sqlOptions.CommandTimeout(30);
        
        // 迁移程序集
        sqlOptions.MigrationsAssembly(typeof(AppDbContext).Assembly.FullName);
    }));

SQL Server 特有功能 ​

1. 行版本(乐观并发) ​

csharp
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

// 生成的列:
// [RowVersion] ROWVERSION NOT NULL

2. 计算列 ​

csharp
modelBuilder.Entity<Order>(entity =>
{
    entity.Property(o => o.TotalAmount)
        .HasComputedColumnSql("[Quantity] * [UnitPrice]", stored: true);
});

3. 序列(Sequence) ​

csharp
// 创建序列
modelBuilder.HasSequence<int>("OrderNumberSequence")
    .StartsAt(1000)
    .IncrementsBy(1);

// 使用序列
modelBuilder.Entity<Order>(entity =>
{
    entity.Property(o => o.OrderNumber)
        .HasDefaultValueSql("NEXT VALUE FOR OrderNumberSequence");
});

4. 临时表(Temporal Tables) ​

csharp
modelBuilder.Entity<Product>(entity =>
{
    entity.ToTable(b => b.IsTemporal());
});

// 查询历史数据
var history = await context.Products
    .TemporalAsOf(DateTime.UtcNow.AddDays(-7))
    .Where(p => p.Id == 1)
    .ToListAsync();

5. 全文搜索 ​

csharp
var products = await context.Products
    .Where(p => EF.Functions.FreeText(p.Description, "laptop computer"))
    .ToListAsync();

// 生成的 SQL:
// WHERE FREETEXT([p].[Description], N'laptop computer')

PostgreSQL ​

安装 ​

bash
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite  # GIS 支持(可选)

配置 ​

csharp
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseNpgsql(connectionString, npgsqlOptions =>
    {
        npgsqlOptions.EnableRetryOnFailure(
            maxRetryCount: 3,
            maxRetryDelay: TimeSpan.FromSeconds(10));
        
        npgsqlOptions.CommandTimeout(30);
        npgsqlOptions.MigrationsAssembly(typeof(AppDbContext).Assembly.FullName);
    }));

PostgreSQL 特有功能 ​

1. JSONB 支持 ​

csharp
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    // JSONB 列
    public JsonDocument Specifications { get; set; }
}

// 配置
modelBuilder.Entity<Product>(entity =>
{
    entity.Property(p => p.Specifications)
        .HasColumnType("jsonb");
});

// 查询 JSON 字段
var products = await context.Products
    .Where(p => p.Specifications.RootElement.GetProperty("color").GetString() == "red")
    .ToListAsync();

2. 数组类型 ​

csharp
public class Blog
{
    public int Id { get; set; }
    public string[] Tags { get; set; }
}

// 配置
modelBuilder.Entity<Blog>(entity =>
{
    entity.Property(b => b.Tags)
        .HasColumnType("text[]");
});

// 查询数组
var blogs = await context.Blogs
    .Where(b => b.Tags.Contains("ef-core"))
    .ToListAsync();

3. 范围类型 ​

csharp
public class Event
{
    public int Id { get; set; }
    public NpgsqlRange<DateTime> Duration { get; set; }
}

// 查询重叠的时间范围
var events = await context.Events
    .Where(e => e.Duration.Overlaps(
        new NpgsqlRange<DateTime>(startDate, endDate)))
    .ToListAsync();

4. ILIKE (不区分大小写搜索) ​

csharp
var products = await context.Products
    .Where(p => EF.Functions.ILike(p.Name, "%laptop%"))
    .ToListAsync();

// 生成的 SQL:
// WHERE [p].[Name] ILIKE '%laptop%'

5. PostGIS (地理空间数据) ​

bash
dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL.NetTopologySuite
csharp
using NetTopologySuite.Geometries;

public class Location
{
    public int Id { get; set; }
    public Point Coordinates { get; set; }
}

// 配置
modelBuilder.Entity<Location>(entity =>
{
    entity.Property(l => l.Coordinates)
        .HasColumnType("geometry(point)");
});

// 查询附近的点
var userLocation = new Point(longitude, latitude) { SRID = 4326 };
var nearby = await context.Locations
    .OrderBy(l => l.Coordinates.Distance(userLocation))
    .Take(10)
    .ToListAsync();

MySQL ​

安装 ​

bash
dotnet add package Pomelo.EntityFrameworkCore.MySql

配置 ​

csharp
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseMySql(connectionString, 
        ServerVersion.AutoDetect(connectionString), 
        mysqlOptions =>
    {
        mysqlOptions.EnableRetryOnFailure(
            maxRetryCount: 3,
            maxRetryDelay: TimeSpan.FromSeconds(10));
        
        mysqlOptions.CommandTimeout(30);
        mysqlOptions.MigrationsAssembly(typeof(AppDbContext).Assembly.FullName);
    }));

MySQL 特有功能 ​

1. 字符集配置 ​

csharp
modelBuilder.Entity<Product>(entity =>
{
    entity.UseCollation("utf8mb4_unicode_ci");
});

2. 自增主键 ​

csharp
public class Product
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
}

// 生成的 SQL:
// `Id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY

3. FULLTEXT 索引 ​

csharp
modelBuilder.Entity<Article>(entity =>
{
    entity.HasIndex(a => a.Content)
        .HasMethod("FULLTEXT");
});

// 全文搜索
var articles = await context.Articles
    .Where(a => EF.Functions.Match(a.Content, "search term"))
    .ToListAsync();

4. JSON 支持(MySQL 5.7+) ​

csharp
public class Product
{
    public int Id { get; set; }
    public string Specifications { get; set; }  // JSON 字符串
}

// 查询 JSON 字段
var products = await context.Products
    .Where(p => EF.Functions.JsonContains(
        p.Specifications, 
        "{\"color\": \"red\"}"))
    .ToListAsync();

SQLite ​

安装 ​

bash
dotnet add package Microsoft.EntityFrameworkCore.Sqlite

配置 ​

csharp
// 开发/测试环境
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlite("Data Source=app.db"));

// 内存数据库(测试用)
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlite("Data Source=:memory:"));

SQLite 特有考虑 ​

1. 并发限制 ​

csharp
// ⚠️ SQLite 默认不支持高并发写入
// 解决方案: 启用 WAL 模式

var connection = new SqliteConnection(connectionString);
connection.Open();
using var command = connection.CreateCommand();
command.CommandText = "PRAGMA journal_mode=WAL;";
command.ExecuteNonQuery();

2. 数据类型灵活性 ​

csharp
// SQLite 是弱类型,所有整数都是 INTEGER
// 所有浮点数都是 REAL
// 注意: 不会强制检查类型约束

public class Product
{
    public int Id { get; set; }           // INTEGER
    public decimal Price { get; set; }    // REAL
    public DateTime CreatedAt { get; set; } // TEXT (ISO 8601)
}

3. 迁移限制 ​

csharp
// ⚠️ SQLite 不支持某些 ALTER TABLE 操作
// 例如: 删除列、修改列类型

// 解决方案: 重新创建表
// dotnet ef migrations add MigrationName
// 手动编辑迁移文件,使用 RebuildTable 模式

4. 内存数据库测试 ​

csharp
[Fact]
public async Task TestWithInMemorySqlite()
{
    var connection = new SqliteConnection("Data Source=:memory:");
    connection.Open();
    
    var options = new DbContextOptionsBuilder<AppDbContext>()
        .UseSqlite(connection)
        .Options;
    
    using var context = new AppDbContext(options);
    await context.Database.EnsureCreatedAsync();
    
    // 执行测试...
    
    connection.Close();
}

Cosmos DB ​

安装 ​

bash
dotnet add package Microsoft.EntityFrameworkCore.Cosmos

配置 ​

csharp
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseCosmos(
        "https://localhost:8081",
        "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
        databaseName: "MyDatabase"));

Cosmos DB 特性 ​

1. 无架构设计 ​

csharp
public class Order
{
    public string Id { get; set; }  // 必须是 string
    public string CustomerId { get; set; }
    public List<OrderItem> Items { get; set; }
}

// 存储为 JSON 文档:
// {
//   "id": "order-123",
//   "customerId": "cust-456",
//   "items": [...],
//   "_rid": "...",
//   "_ts": 1234567890
// }

2. 分区键 ​

csharp
modelBuilder.Entity<Order>(entity =>
{
    entity.ToContainer("Orders");
    entity.HasPartitionKey(o => o.CustomerId);  // 分区键
    entity.HasKey(o => o.Id);
});

3. 嵌入文档 ​

csharp
public class Order
{
    public string Id { get; set; }
    public Address ShippingAddress { get; set; }  // 嵌入对象
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
}

// 存储为嵌套 JSON:
// {
//   "id": "order-123",
//   "shippingAddress": {
//     "street": "123 Main St",
//     "city": "Seattle"
//   }
// }

多数据库支持 ​

策略 1: 条件注册 ​

csharp
var dbType = builder.Configuration["Database:Type"];

if (dbType == "SqlServer")
{
    builder.Services.AddDbContext<AppDbContext>(options =>
        options.UseSqlServer(connectionString));
}
else if (dbType == "PostgreSQL")
{
    builder.Services.AddDbContext<AppDbContext>(options =>
        options.UseNpgsql(connectionString));
}
else if (dbType == "MySQL")
{
    builder.Services.AddDbContext<AppDbContext>(options =>
        options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)));
}

策略 2: 抽象工厂 ​

csharp
public interface IDbContextFactory
{
    AppDbContext Create();
}

public class SqlServerDbContextFactory : IDbContextFactory
{
    private readonly string _connectionString;
    
    public AppDbContext Create()
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseSqlServer(_connectionString)
            .Options;
        return new AppDbContext(options);
    }
}

public class PostgreSqlDbContextFactory : IDbContextFactory
{
    private readonly string _connectionString;
    
    public AppDbContext Create()
    {
        var options = new DbContextOptionsBuilder<AppDbContext>()
            .UseNpgsql(_connectionString)
            .Options;
        return new AppDbContext(options);
    }
}

策略 3: 避免数据库特定代码 ​

csharp
// ❌ 错误: 使用 SQL Server 特定函数
.Where(p => SqlFunctions.DateDiff("day", p.CreatedAt, DateTime.Now) > 30)

// ✅ 正确: 使用标准 LINQ
.Where(p => EF.Functions.DateDiffDay(p.CreatedAt, DateTime.Now) > 30)

// ✅ 更好: 完全可移植
var cutoffDate = DateTime.Now.AddDays(-30);
.Where(p => p.CreatedAt < cutoffDate)

提供程序对比 ​

功能支持矩阵 ​

功能SQL ServerPostgreSQLMySQLSQLite
LINQ 翻译✅ 完整✅ 完整✅ 完整✅ 基本
迁移支持✅ 完整✅ 完整✅ 完整⚠️ 部分
并发控制✅ RowVersion✅ xmin⚠️ 有限❌ 无
JSON 支持✅ JSON✅ JSONB✅ JSON⚠️ TEXT
全文搜索✅ FreeText✅ tsvector✅ MATCH❌ 无
空间数据✅ Geography✅ PostGIS⚠️ 有限⚠️ 有限
批量操作✅ 优秀✅ 优秀✅ 良好⚠️ 一般
性能(读)⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
性能(写)⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
并发能力⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

选择建议 ​

项目需求
│
├─ Windows 企业环境?
│  └─ ✅ SQL Server
│
├─ Linux/云原生?
│  └─ ✅ PostgreSQL
│
├─ Web 应用/WordPress 迁移?
│  └─ ✅ MySQL
│
├─ 移动/桌面应用?
│  └─ ✅ SQLite
│
├─ 需要 NoSQL/文档模型?
│  └─ ✅ Cosmos DB
│
└─ 不确定?
   └─ ✅ PostgreSQL (最佳性价比)

最佳实践 ​

✅ 推荐做法 ​

1. 使用连接字符串配置 ​

json
// appsettings.json
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=MyApp;..."
  },
  "Database": {
    "Type": "SqlServer",
    "EnableRetry": true,
    "CommandTimeout": 30
  }
}

2. 启用重试策略(生产环境) ​

csharp
options.UseSqlServer(connectionString, sqlOptions =>
    sqlOptions.EnableRetryOnFailure(
        maxRetryCount: 3,
        maxRetryDelay: TimeSpan.FromSeconds(10)));

3. 避免数据库特定代码 ​

csharp
// ✅ 使用标准 EF Core API
modelBuilder.Entity<Product>(entity =>
{
    entity.Property(p => p.Name).IsRequired();
    entity.HasIndex(p => p.Name).IsUnique();
});

// ❌ 避免直接使用原生 SQL 特性

4. 测试时使用 SQLite ​

csharp
// 单元测试
var options = new DbContextOptionsBuilder<AppDbContext>()
    .UseSqlite("Data Source=:memory:")
    .Options;

using var context = new AppDbContext(options);
await context.Database.EnsureCreatedAsync();

❌ 常见陷阱 ​

1. 忽略提供程序差异 ​

csharp
// ❌ SQL Server 语法
.Take(10)  // 生成 TOP 10

// ⚠️ 在不同数据库中行为可能不同
// 始终测试目标数据库

2. 不使用参数化查询 ​

csharp
// ❌ SQL 注入风险
context.Products.FromSqlRaw($"SELECT * FROM Products WHERE Name = '{name}'");

// ✅ 参数化
context.Products.FromSqlRaw("SELECT * FROM Products WHERE Name = {0}", name);

3. 忽略时区问题 ​

csharp
// ❌ DateTime 可能有时区问题
.Where(p => p.CreatedAt > DateTime.Now)

// ✅ 使用 UTC
.Where(p => p.CreatedAt > DateTime.UtcNow)

总结 ​

提供程序选择决策树 ​

开始
│
├─ 已有数据库?
│  └─ 使用该数据库的提供程序
│
├─ 新项目?
│  ├─ Windows 企业 → SQL Server
│  ├─ Linux/云 → PostgreSQL
│  ├─ Web 托管 → MySQL
│  ├─ 移动/桌面 → SQLite
│  └─ 全球 scale → Cosmos DB
│
└─ 需要多数据库支持?
   └─ 抽象 DbContext 配置,避免数据库特定代码

核心要点 ​

  1. 选择合适的提供程序: 根据部署环境和需求
  2. 启用重试策略: 提高系统韧性
  3. 避免数据库锁定: 使用标准 EF Core API
  4. 测试目标数据库: 不要只在 InMemory 测试
  5. 监控性能差异: 不同数据库性能特征不同

基于 MIT 许可发布