还在dotnet framework 2.0的时代,当时还没有EF,而NHibernate之类的又太复杂,并且自己也有一些特殊需求,如查询结果直接入表、水平分表和新增数据默认值等,就试着折腾个轻量点ORM框架,就慢慢有了这个Light.Data,也一直在公司和个人的项目使用,后来陆陆续续也支持了跨数据库并在mono中使用。到了dotnet core来临,也尝试移植,1.0的时候由于类库不完善,效果不理想。2.0的基本完善了,由于个人严重的强迫症和拖延症,一直在折腾细节、写单元测试和磨文档,磨到差不多3.0快来了。。。。
不说废话了,Light.Data
是一个轻量级的基于dotnet standard 2.0
的ORM框架, 通过对实体模型类的Attribute
或者配置文件进行配置与数据表的对应关系. 使用核心类DataContext
对数据表进行CURD
的操作.
PM> Install-Package Light.Data
支持数据库
数据库 | 说明 |
---|---|
SqlServer | 安装Light.Data.Mssql 类库, 支持SqlServer 2008或以上 |
Mysql | 安装Light.Data.Mysql 类库, 支持Mysql 5.5或以上 |
Postgre | 安装Light.Data.Postgre 类库, 支持Postgre9.3或以上 |
-
使用文档: https://aquilahkj.github.io/Light.Data.Site/#/zh-cn/
-
Github: https://github.com/aquilahkj/Light.Data2
连接配置
{ "lightData": { "connections": [ { "name": "mssql_db", "connectionString": "...", "providerName": "Light.Data.Mssql.MssqlProvider, Light.Data.Mssql" }, { "name": "mysq_db", "connectionString": "...", "providerName": "Light.Data.Mysql.MysqlProvider, Light.Data.Mysql" } ] } }
使用方式
// 直接使用 DataContext context = new DataContext("mssql"); // 创建子类 public class MyDataContext : DataContext { public MyDataContext() : base("mssql") { } } // 创建配置子类 public class MyDataContext : DataContext { public MyDataContext(DataContextOptions<MyDataContext> options) : base(options) { } } // 直接配置连接字符串和参数 (IServiceCollection) service.AddDataContext<MyDataContext>(builder => { builder.UseMssql(connectionString); builder.SetTimeout(2000); builder.SetVersion("11.0"); }, ServiceLifetime.Transient); // 默认配置文件配置 (IServiceCollection) service.AddDataContext<MyDataContext>(DataContextConfiguration.Global, config => { config.ConfigName = "mssql"; }, ServiceLifetime.Transient);
对象映射
1 [DataTable("Te_User", IsEntityTable = true)] 2 public class TeUser 3 { 4 /// <summary> 5 /// Id 6 /// </summary> 7 /// <value></value> 8 [DataField("Id", IsIdentity = true, IsPrimaryKey = true)] 9 public int Id 10 { 11 get; 12 set; 13 } 14 15 /// <summary> 16 /// Account 17 /// </summary> 18 /// <value></value> 19 [DataField("Account")] 20 public string Account 21 { 22 get; 23 set; 24 } 25 26 /// <summary> 27 /// Telephone 28 /// </summary> 29 /// <value></value> 30 [DataField("Telephone", IsNullable = true)] 31 public string Telephone 32 { 33 get; 34 set; 35 } 36 .... 37 }
子表对象关联
1 [DataTable("Te_UserExtend", IsEntityTable = true)] 2 public class TeUserExtend 3 { 4 [DataField("Id", IsIdentity = true, IsPrimaryKey = true)] 5 public int Id 6 { 7 get; 8 set; 9 } 10 11 [DataField("MainId")] 12 public int MainId 13 { 14 get; 15 set; 16 } 17 18 [DataField("Data", IsNullable = true)] 19 public string Data 20 { 21 get; 22 set; 23 } 24 } 25 26 27 public class TeUserAndExtend : TeUser 28 { 29 [RelationField("Id", "MainId")] 30 public TeUserExtend Extend 31 { 32 get; 33 set; 34 } 35 }
基本操作
- 基本CURD
- 批量CUD
- 支持事务处理
- 支持数据字段默认值和自动时间戳
- 支持数据字段读写控制
- 查询结果指定类或匿名类输出
- 查询直接插入数据表
1 var context = new DataContext(); 2 // 查询单个数据 3 var item = context.Query<TeUser>().Where(x => x.Id == 10).First(); 4 // 查询集合数据 5 var list = context.Query<TeUser>().Where(x => x.Id > 10).ToList(); 6 // 新增数据 7 var user = new TeUser() { 8 Account = "foo", 9 Password = "bar" 10 }; 11 context.Insert(user); 12 // 修改数据 13 user.Password = "bar1"; 14 context.Update(user); 15 // 删除数据 16 context.Delete(user);
数据汇总
- 单列数据直接汇总
- 多列数据分组汇总
- 格式化分组字段
- 汇总数据直接插入数据表
1 // 普通汇总 2 var list = context.Query<TeUser> () 3 .Where (x => x.Id >= 5) 4 .Aggregate (x => new LevelIdAgg () { 5 LevelId = x.LevelId, 6 Data = Function.Count () 7 }) 8 .ToList (); 9 10 // 日期格式化统计 11 var list = context.Query<TeUser> () 12 .Aggtrgate (x => new RegDateFormatAgg () { 13 RegDateFormat = x.RegTime.ToString("yyyy-MM-dd"), 14 Data = Function.Count () 15 }) 16 .ToList ();
连表查询
- 多表连接, 支持内连接, 左连接和右连接
- 支持查询结果和汇总数据连接
- 连接查询结果指定类或匿名类输出
- 连接查询结果直接插入数据表
1 // 内连接 2 var join = context.Query<TeUser> () 3 .Join<TeUserExtend>((x,y) => x.Id == y.Id); 4 5 // 统计结果连接实体表 6 var join = context.Query<TeMainTable>() 7 .GroupBy(x => new { 8 MId = x.MId, 9 Count = Function.Count(), 10 }) 11 .Join<TeSubTable>((x, y) => x.MId == y.Id);
执行SQL语句
- 直接使用SQL语句和存储过程
- 支持对象参数
- 查询结果指定类或匿名类输出
- 存储过程支持output参数
1 // 普通参数 2 var sql = "update Te_User set NickName=@P2 where Id=@P1"; 3 var ps = new DataParameter[2]; 4 ps[0] = new DataParameter("P1", 5); 5 ps[1] = new DataParameter("P2", "abc"); 6 var executor = context.CreateSqlStringExecutor(sql, ps); 7 var ret = executor.ExecuteNonQuery(); 8 9 // 对象参数 10 var sql = "update Te_User set NickName={nickname} where Id={id}"; 11 var executor = context.CreateSqlStringExecutor(sql, new { nickname = "abc", id = 5 }); 12 var ret = executor.ExecuteNonQuery();
单元测试
项目使用xUnit做单元测试,测试代码地址:https://github.com/aquilahkj/Light.Data2/tree/master/test
每种数据库均有300多组1000多用例的测试,覆盖大部分代码。
性能测试
目前只跟EF Core在同一电脑的Docker上的Sql Server 2017 for linux中做简单的增删改查性能对比测试,代码地址 https://github.com/aquilahkj/OrmTest
1000次的增删改和单条数据查询
共5轮,每轮1000条的增删改和1000条数据查询
EF的测试结果
Light.Data测试结果
从对比看,查询性能两者差不多,新增性能Light.Data稍微占优,批量更新也稍微占优。
另外值得一提的是Postgre,批量增删改性能比Sql Server快2,3倍,看来以后也要好好研究一下。
以上均是非严谨测试,仅供参考。
最后
本文只是简单的介绍,具体使用方法可以查看文档和参考测试用例,如有需要会写具体使用的文章。
Light.Data这个项目这么多年来个人一直维护,也在不断优化,不断成长,但一直养在深闺,好不容易折腾开源也是希望能共享出去,给有需要的朋友多个选择,同时也是给自己这么多年码农的见证。
虽然现在ORM的框架非常非常多,也不是什么热门新鲜事物,但总归是个基础的东西。
如果有朋友喜欢,不妨试试,可以的话,给个Star,十分欢迎意见或建议 :D