FreeSQL 是一款专为 .NET 开发者设计的轻量级 ORM(对象关系映射)框架,它通过将数据库表映射为 .NET 对象,让开发者可以用面向对象的方式操作数据库。作为一个在 .NET 生态中成长起来的开源项目,FreeSQL 以其简洁的 API 设计和强大的功能特性赢得了不少开发者的青睐。
提示:ORM 框架的核心价值在于消除对象模型与关系数据库之间的"阻抗不匹配",让开发者专注于业务逻辑而非数据库操作细节。
FreeSQL 能在众多 .NET ORM 框架中脱颖而出,主要得益于以下几个关键特性:
多数据库支持:不同于某些只能对接特定数据库的 ORM,FreeSQL 提供了对 MySQL、SQL Server、PostgreSQL、Oracle 等主流数据库的统一访问接口。这意味着当你的项目需要切换数据库时,大部分数据访问代码可以保持不变。
轻量级设计:核心包体积控制在合理范围内,不引入过多依赖,这使得它在中小型项目中表现尤为出色。相比一些功能臃肿的 ORM,FreeSQL 保持了恰到好处的功能集。
CodeFirst 开发模式:允许开发者先定义实体类,然后由框架自动生成数据库表结构。这种开发流程特别适合快速迭代的项目,可以显著提升开发效率。
LINQ 支持:提供了完整的 LINQ 提供程序实现,开发者可以使用熟悉的 LINQ 语法编写查询,框架会将其转换为目标数据库的 SQL 方言。
性能优化:在批量操作、缓存机制等方面做了针对性优化,避免了常见 ORM 的性能陷阱。
根据实际项目经验,FreeSQL 特别适合以下场景:
创建一个新的 .NET 6 控制台应用项目(Web 项目配置方式类似),然后通过 NuGet 包管理器添加必要的依赖:
bash复制dotnet add package FreeSql
dotnet add package FreeSql.Provider.MySql
对于不同的数据库,需要选择对应的 Provider 包:
FreeSql.Provider.SqlServerFreeSql.Provider.PostgreSQLFreeSql.Provider.OracleFreeSql.Provider.Sqlite推荐使用单例模式管理 FreeSQL 实例,这不仅能避免重复创建连接带来的性能开销,还能确保全局配置的一致性。以下是典型的单例实现:
csharp复制public static class FreeSqlFactory
{
private static readonly Lazy<IFreeSql> _instance = new Lazy<IFreeSql>(() =>
{
var builder = new FreeSqlBuilder()
.UseConnectionString(DataType.MySql,
"Server=localhost;Port=3306;User ID=root;Password=123456;Database=freesql_demo;Charset=utf8mb4;")
.UseAutoSyncStructure(true)
.UseMonitorCommand(cmd => Console.WriteLine($"SQL: {cmd.CommandText}"));
return builder.Build();
});
public static IFreeSql Instance => _instance.Value;
}
关键配置说明:
UseAutoSyncStructure: 开发环境建议开启,生产环境务必关闭UseMonitorCommand: 可选配置,用于输出执行的 SQL 语句,便于调试FreeSQL 的实体类设计遵循"约定优于配置"的原则,以下是一个符合最佳实践的实体类示例:
csharp复制[Table(Name = "sys_user")]
public class User
{
[Column(IsPrimary = true, IsIdentity = true)]
public long Id { get; set; }
[Column(IsNullable = false, StringLength = 50)]
public string UserName { get; set; }
[Column(IsNullable = false, StringLength = 100)]
public string PasswordHash { get; set; }
[Column(IsNullable = true)]
public int? Age { get; set; }
[Column(IsNullable = true, StringLength = 100)]
public string Email { get; set; }
[Column(ServerTime = DateTimeKind.Utc)]
public DateTime CreateTime { get; set; }
[Column(IsVersion = true)]
public long Version { get; set; }
}
实体类设计要点:
FreeSQL 提供了多种插入数据的方式,满足不同场景需求:
csharp复制// 单条插入
var user = new User { UserName = "admin", PasswordHash = "hashed_pwd" };
var inserted = fsql.Insert(user).ExecuteAffrows();
// 批量插入(自动分批)
var users = new List<User> { /* 多个用户 */ };
var affected = fsql.Insert(users).ExecuteAffrows();
// 插入后获取自增ID
var newUser = fsql.Insert(user).ExecuteInserted().FirstOrDefault();
Console.WriteLine($"New ID: {newUser.Id}");
更新操作支持多种模式,推荐使用局部更新以提升性能:
csharp复制// 局部更新(推荐)
fsql.Update<User>()
.Set(u => u.Age, 30)
.Set(u => u.Email, "updated@example.com")
.Where(u => u.Id == 1)
.ExecuteAffrows();
// 全量更新(先查询再更新)
var user = fsql.Select<User>().Where(u => u.Id == 1).First();
user.Age = 30;
fsql.Update(user).ExecuteAffrows();
FreeSQL 的查询功能非常强大,支持各种复杂场景:
csharp复制// 基础查询
var users = fsql.Select<User>().ToList();
// 条件查询
var adults = fsql.Select<User>()
.Where(u => u.Age >= 18)
.ToList();
// 分页查询
var page = fsql.Select<User>()
.OrderByDescending(u => u.CreateTime)
.Page(2, 10) // 第2页,每页10条
.ToList();
// 投影查询(只选择需要的字段)
var names = fsql.Select<User>()
.Where(u => u.Age > 20)
.Select(u => new { u.Id, u.UserName })
.ToList();
删除操作需要特别注意添加条件,避免误删:
csharp复制// 条件删除
fsql.Delete<User>()
.Where(u => u.Id == 1)
.ExecuteAffrows();
// 批量删除
fsql.Delete<User>()
.Where(u => u.Age < 18)
.ExecuteAffrows();
FreeSQL 提供了流畅的 API 支持复杂联表查询:
csharp复制var query = fsql.Select<User>()
.LeftJoin<Department>((u, d) => u.DepartmentId == d.Id)
.Where((u, d) => d.Name == "研发部")
.ToList();
子查询可以构建更复杂的查询逻辑:
csharp复制var subQuery = fsql.Select<User>()
.Where(u => u.Age > 30)
.As("t");
var result = fsql.Select<User>()
.From(subQuery)
.Where("t.Age < 40")
.ToList();
当 LINQ 无法满足需求时,可以直接使用 SQL:
csharp复制var users = fsql.Ado.Query<User>("SELECT * FROM sys_user WHERE age > @age",
new { age = 20 });
FreeSQL 提供了多种事务管理方式,UnitOfWork 是最推荐的方式:
csharp复制using (var uow = fsql.CreateUnitOfWork())
{
try
{
// 操作1
fsql.Insert(new User { /* ... */ }).WithTransaction(uow).ExecuteAffrows();
// 操作2
fsql.Update<User>(/* ... */).WithTransaction(uow).ExecuteAffrows();
uow.Commit();
}
catch
{
uow.Rollback();
throw;
}
}
FreeSQL 内置支持读写分离配置:
csharp复制var builder = new FreeSqlBuilder()
.UseConnectionString(DataType.MySql, "写库连接字符串")
.UseSlave("从库1连接字符串")
.UseSlave("从库2连接字符串");
可以通过 AOP 机制实现数据审计:
csharp复制fsql.Aop.CurdAfter += (s, e) =>
{
if (e.ElapsedMilliseconds > 200)
{
Console.WriteLine($"慢查询: {e.Sql}");
}
};
csharp复制// 批量插入优化
fsql.Insert(users).BatchSize(1000).ExecuteAffrows();
// 批量更新优化
fsql.Update<User>()
.Set(u => u.Status, 1)
.Where(u => ids.Contains(u.Id))
.ExecuteAffrows();
csharp复制// 查询缓存
var users = fsql.Select<User>()
.Where(u => u.Status == 1)
.WithCache(TimeSpan.FromMinutes(5))
.ToList();
对于复杂查询,可以提供索引提示:
csharp复制var users = fsql.Select<User>()
.WithIndex("idx_status_created")
.Where(u => u.Status == 1)
.ToList();
对于已有项目迁移到 FreeSQL:
建议的监控指标:
在实际项目中使用 FreeSQL 时,建议从小规模开始逐步验证,确保框架特性与项目需求匹配。对于复杂的业务场景,可以先在测试环境充分验证各种边界情况。