.NET Core ORM框架之 - FreeSql

698 阅读5分钟
  • 本文已参与「新人创作礼」活动,一起开启掘金创作之路。

FreeSql是什么?

FreeSql 是一款功能强大的对象关系映射(O/RM)组件!为什么要使用FreeSql呢?官方给出了下面优势,通过我个人使用来看,还是非常的不错的,功能也非常强大,就是语法让我非常的不习惯,感觉像Linq又像再写Sql,只能说他的语法非常丰富且强大.....。

  • 🛠 支持 CodeFirst 模式,即便使用 Access 数据库也支持数据迁移;
  • 💻 支持 DbFirst 模式,支持从数据库导入实体类,或使用实体类生成工具生成实体类;
  • ⛳ 支持 深入的类型映射,比如 PgSql 的数组类型等;
  • ✒ 支持 丰富的表达式函数,以及灵活的自定义解析;
  • 🏁 支持 导航属性一对多、多对多贪婪加载,以及延时加载;
  • 📃 支持 读写分离、分表分库、过滤器、乐观锁、悲观锁;
  • 🌳 支持 MySql/SqlServer/PostgreSQL/Oracle/Sqlite/Firebird/达梦/人大金仓/神舟通用/南大通用/翰高/ClickHouse/Access 等数据库;

如何在.NET Core中使用FreeSql?

本文代码都是单一项目结构,没有分层没有任何架构,使用的时候可以根据自己项目结构或者架构合理将ORM放入到适合的模块,有利于开发人员更好阅读以及理解项目。

主要依赖的包有FreeSql 以及 FreeSql.Provider.SqlServer

声明

新增FreeSqlDb类,用于声明FreeSql。包括了多个方法,下面代码只使用了两个,还有一些可以阅读官方文档按需使用

public class FreeSqlDb
{
    public IFreeSql freeSql { get; private set; }
    public FreeSqlDb() 
    {
        freeSql = new FreeSql.FreeSqlBuilder()
          //连接串 1.为数据库类型,2.为数据库连接串
          .UseConnectionString(FreeSql.DataType.SqlServer, @"server=localhost;database=Book")
          //自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改[生产环境慎用]
          .UseAutoSyncStructure(true)
          .Build();
    }
}

//在项目初始化StartUp类或者Program类初始化FreeSql
//要定义成单例模式,而不是在每次使用的时候创建。
builder.Services.AddSingleton<FreeSqlDb>();

CodeFirst

FreeSql支持Codefirst,这当然也是ORM框架的标配。它还提供了许多特性,正如FreeSql文中所写:优点是充分利用数据库特性辅助开发,缺点是切换数据库变得困难。如何使用Code First呢?FreeSql提供了两种实现方式,第一种就是在初始化的时候UseAutoSyncStructure置为True实现自动迁移,第二种是手动。下面为大家展示用法。

  • 新增Model文件夹,用于存放项目所使用的所有实体类,在文件夹新增实体类Book,实体所标注的特性由FreeSql提供
[Table(Name = "tb_book")] //实体特性,当表名称与实体名称各自命名无法对应,可以使用Table特性
public class Book
{

    //IsPrimary 如果没有指定主键 系统会自动将Id命名的字段设为主键 IsIdentity 设置自增
    [Column(IsPrimary = true,IsIdentity = true)] 
    public int Id { get; set; }
    
    [Column(DbType = "varchar(128) NOT NULL")] // 指定数据库类型
    public string BookName { get; set; }
    
    [Column(StringLength = -1)] // 当数据库存放字符串长度过长可以使用 等比于nvarchar(max)
    public string Description { get; set; }
    
    [Column(DbType = "decimal(10,2)")]
    public decimal Price { get; set; }
    
    // 服务器时间类似于在插入的时候getdate(),CanUpdate 表示更新的时候忽略这个字段
    [Column(ServerTime = DateTimeKind.Utc, CanUpdate = false)] 
    public DateTime CreateTime { get; set; }
}
  • 新增BookController,创建Add方法,我们一起来测试看看数据是否能新增成功,会不会为我们生成表呢!
// 注入我们之前初始化 FreeSqlDb 类
private readonly FreeSqlDb _freeSql;
public BookController(FreeSqlDb freeSql) 
{
    _freeSql = freeSql;
}

[HttpPost]
public async Task Add(Book book) 
{
    // 执行Insert方法,返回受影响行数
    var count = await _freeSql.freeSql.Insert(book).ExecuteAffrowsAsync();

    if (count <= 0)
        throw new Exception("数据库新增失败,请仔细检查!");
}

image.png 通过上图可以看出,将我们模型中配置的特性已经得到了体现,为我们生成了表,数据也新增成功了!

注意:FreeSql不会帮你生成数据库,需要你手动创建数据库,需要自动创建数据库.请参考此代码FreeSqlExtension.cs,关于另外一种手动生成,本文就不在演示,相信观看上面方法,实现起来也是非常容易的。

查询

FreeSql 在查询数据下足了功夫,链式查询语法、多表查询、表达式函数支持得非常到位。

[HttpGet]
public async Task Get(int page,int limit) 
{
    // 分页查询
    var list = await _freeSql.freeSql.Select<Book>()
                               .Where(a => a.Id > 10) //查询条件
                               .OrderBy(a => a.CreateTime) //为了分页次序明确,建议先排序再Count
                               .Count(out var total) //总记录数量
                               .Page(page, limit)
                               .ToListAsync();
    // 连表查询
    var joinList = await _freeSql.freeSql.Select<Book, BookType>()
                                 //配置了导航属性,则不需要手工调用LeftJoin
                                 .LeftJoin((a, b) => a.TypeId == b.Id) 
                                 .ToListAsync((a, b) => new { a, b });

    // 查询单条数据
    Book book = await _freeSql.freeSql.Select<Book>().ToOneAsync();
    
    // withsql
    await _freeSql.freeSql.Select<Book>()
                  .WithSql("select * from Book where id > @val", new { val = 10 })
                  .Page(1, 10)
                  .ToList()
}

不得不说,查询方法真是非常的强大,本文就不一一展示,感兴趣的朋友赶紧上手试试吧查询文档

丰富的表达式

  • 查找今天创建的数据
var list = await _freeSql.freeSql.Select<Book>()
                         .Where(a => a.CreateTime.Date == DateTime.Today)
                         .ToListAsync();
  • In查询
var list = await _freeSql.freeSql.Select<Book>()
             .Where(a => new[] { 1, 2, 3 }.Contains(a.Id))
             .ToList();

这里就简单的列举了几个,确实是太多了!!!

事务

FreeSql支持多种事务,包括 UnitOfWork 事务、 DbContext 事务、同线程事务、外部事务等等,还支持分布式事务,我未作过多了解这里就不给大家说明了,感兴趣可以阅读相关源码、文档

_freeSql.freeSql.Transaction(() => {
    //fsql.Ado.TransactionCurrentThread 获得当前事务对象

    // 新增Book
    var count = _freeSql.freeSql.Insert(book).ExecuteAffrows();

    if (count < 1)
        throw new Exception("新增失败");
    //抛出异常,回滚事务,事务退出

    var affrows = _freeSql.freeSql.Insert<Notice>(notice)
                                  .ExecuteAffrows();
    // 给通知中心存取记录
    if (affrows < 1)
        throw new Exception("新增失败");

    //抛出异常,回滚事务,事务退出
});

上面代码展示的是同线程事务,每个线程只可开启一个事务连接,他的缺点就是不能使用异步方法。

总结

FreeSql 实现了强大功能的同时,性能没有受到影响,项目中使用反射或耗时的操作都经过了缓存处理。不得不说确实厉害,具体使用还是得看公司技术决策,或者场景来决定是否使用,虽然在性能方面不如Dapper但是在一些功能上面我觉得相比起Dapper还是更加好用的。本文阐述的内容如果有不正确的地方,还请各位朋友们指出,我会及时修改!