【原创】基于.NET的轻量级高性能 ORM - Riz.XFramework
【前言】
接上一篇《【原创】打造基于Dapper的数据访问层》,Dapper在应付多表自由关联、分组查询、匿名查询等应用场景时不免显得吃力,经常要手写SQL语句(或者用工具生成SQL配置文件)。试想一下,项目中整个DAL层都塞满了SQL语句,对于后期维护来说无异于天灾人祸,这个坑谁踩谁知道。本框架在API设计上最大程度地借鉴 EntityFramework 的写法,干净的实体,丝滑的增删改查,稳健的导航属性,另外还支持链式查询(点标记)、查询表达式、聚合查询等等。在实体映射转换层面,使用 Emit 来动态构建绑定指令,性能最大限度地接近原生水平。
【XFramework 亮点】
- 原生.NET语法,零学习成本
- 支持LINQ查询、拉姆达表达式
- 支持批量增删改查和多表关联更新
- 支持 SqlServer、MySql、Postgre、Oracle,.NET Core
- 最大亮点,真正支持一对一、一对多导航属性。这一点相信现有开源的ORM没几个敢说它支持的
- 实体字段类型不必与数据库的类型一致
- 支持临时表、表变量操作
- 提供原生ADO操作
- 其它更多亮点,用了你就会知道
【性能】
看看与EntityFramework的性能对比,机器配置不同跑出来的结果可能也不一样,仅供参考。需要特别说明的是EntityFramework是用了AsNoTracking的,不然有缓存的话就没有比较的意义了。
扯个题外话,有些ORM说比EntityFramework快百分多少多少,看起来挺美实际上在我看来那是在扯淡,没有任何参考价值。限制实体字段类型必须与数据库的一致不说,甚至连导航属性这种最基本的功能都不支持,这不在同一个等级同一个体量上的东西非要扯在一起比谁快,确定要这么幽默吗?本人非常推崇开源,也很尊重致力于开源的同行,但是像这种行为就非常有必要出来打假一波了。
【功能说明】
1. 实体定义
1.1. 如果类有 TableAttribute,则用 TableAttribute 指定的名称做为表名,否则用类名称做为表名
1.2. 实体的字段可以指定 ColumnAttribute 特性来说明实体字段与表字段的对应关系,删除/更新时如果传递的参数是一个实体,必须使用 [Column(IsKey = true)] 指定实体的主键
1.3. ForeignKeyAttribute 指定外键,一对多外键时类型必须是 IList<T> 或者 List<T>
1.4 ColumnAttribute.DataType 用来指定表字段类型。以SQLSERVER为例,System.String 默认对应 nvarchar 类型。若是varchar类型,需要指定[Column(DbType= DbType.AnsiString)]
1 [Table(Name = "Bas_Client")] 2 public partial class Client 3 { 4 /// <summary> 5 /// 初始化 <see cref="Client"/> 类的新实例 6 /// </summary> 7 public Client() 8 { 9 this.CloudServerId = 0; 10 this.Qty = 0; 11 this.HookConstructor(); 12 } 13 14 /// <summary> 15 /// 初始化 <see cref="Client"/> 类的新实例 16 /// </summary> 17 public Client(Client model) 18 { 19 this.CloudServerId = 0; 20 this.Qty = 0; 21 this.HookConstructor(); 22 } 23 24 /// <summary> 25 /// clientid 26 /// </summary> 27 [Column(IsKey = true)] 28 public virtual int ClientId { get; set; } 29 30 /// <summary> 31 /// clientcode 32 /// </summary> 33 public virtual string ClientCode { get; set; } 34 35 /// <summary> 36 /// clientname 37 /// </summary> 38 public virtual string ClientName { get; set; } 39 40 /// <summary> 41 /// cloudserverid 42 /// </summary> 43 [Column(Default = 0)] 44 public virtual int CloudServerId { get; set; } 45 46 /// <summary> 47 /// activedate 48 /// </summary> 49 public virtual Nullable<DateTime> ActiveDate { get; set; } 50 51 /// <summary> 52 /// qty 53 /// </summary> 54 [Column(Default = 0)] 55 public virtual int Qty { get; set; } 56 57 /// <summary> 58 /// state 59 /// </summary> 60 public virtual byte State { get; set; } 61 62 /// <summary> 63 /// remark 64 /// </summary> 65 [Column(Default = "'默认值'")] 66 public virtual string Remark { get; set; } 67 68 [ForeignKey("CloudServerId")] 69 public virtual CloudServer CloudServer { get; set; } 70 71 [ForeignKey("CloudServerId")] 72 public virtual CloudServer LocalServer { get; set; } 73 74 [ForeignKey("ClientId")] 75 public virtual List<ClientAccount> Accounts { get; set; } 76 77 /// <summary> 78 /// 构造函数勾子 79 /// </summary> 80 partial void HookConstructor(); 81 }
2. 上下文定义
1 SQLSERVER:var context = new SqlDbContext(connString); 2 MySQL:var context = new MyMySqlDbContext(connString); 3 Postgre:var context = new NpgDbContext(connString); 4 Oracle:var context = new OracleDbContext(connString);
3. 匿名类型
1 //// 匿名类 2 var guid = Guid.NewGuid(); 3 var dynamicQuery = 4 from a in context.GetTable<TDemo>() 5 where a.DemoId <= 10 6 select new 7 { 8 DemoId = 12, 9 DemoCode = a.DemoCode, 10 DemoName = a.DemoName, 11 DemoDateTime_Nullable = a.DemoDateTime_Nullable, 12 DemoDate = sDate, 13 DemoDateTime = sDate, 14 DemoDateTime2 = sDate_null, 15 DemoGuid = guid, 16 DemoEnum = Model.State.Complete, // 枚举类型支持 17 DemoEnum2 = Model.State.Executing, 18 }; 19 var result0 = dynamicQuery.ToList(); 20 21 // 点标记 22 dynamicQuery = context 23 .GetTable<TDemo>() 24 .Where(a => a.DemoId <= 10) 25 .Select(a => new 26 { 27 DemoId = 13, 28 DemoCode = a.DemoCode, 29 DemoName = a.DemoName, 30 DemoDateTime_Nullable = a.DemoDateTime_Nullable, 31 DemoDate = sDate, 32 DemoDateTime = sDate, 33 DemoDateTime2 = sDate_null, 34 DemoGuid = Guid.NewGuid(), 35 DemoEnum = Model.State.Complete, 36 DemoEnum2 = Model.State.Executing 37 }); 38 result0 = dynamicQuery.ToList();
4. 所有字段
1 // Date,DateTime,DateTime2 支持 2 var query = 3 from a in context.GetTable<TDemo>() 4 where a.DemoId <= 10 && a.DemoDate > sDate && a.DemoDateTime >= sDate && a.DemoDateTime2 > sDate 5 select a; 6 var result1 = query.ToList(); 7 // 点标记 8 query = context 9 .GetTable<TDemo>() 10 .Where(a => a.DemoId <= 10 && a.DemoDate > sDate && a.DemoDateTime >= sDate && a.DemoDateTime2 > sDate); 11 result1 = query.ToList();
5. 指定字段
1 // 指定字段 2 query = from a in context.GetTable<TDemo>() 3 where a.DemoId <= 10 4 select new TDemo 5 { 6 DemoId = (int)a.DemoId, 7 DemoCode = (a.DemoCode ?? "N001"), 8 DemoName = a.DemoId.ToString(), 9 DemoDateTime_Nullable = a.DemoDateTime_Nullable, 10 DemoDate = sDate, 11 DemoDateTime = sDate, 12 DemoDateTime2 = sDate 13 }; 14 result1 = query.ToList(); 15 // 点标记 16 query = context 17 .GetTable<TDemo>() 18 .Where(a => a.DemoCode != a.DemoId.ToString() && a.DemoName != a.DemoId.ToString() && a.DemoChar == 'A' && a.DemoNChar == 'B') 19 .Select(a => new TDemo 20 { 21 DemoId = a.DemoId, 22 DemoCode = a.DemoName == "张三" ? "李四" : "王五", 23 DemoName = a.DemoCode == "张三" ? "李四" : "王五", 24 DemoChar = 'A', 25 DemoNChar = 'B', 26 DemoDateTime_Nullable = a.DemoDateTime_Nullable, 27 DemoDate = sDate, 28 DemoDateTime = sDate, 29 DemoDateTime2 = sDate 30 }); 31 result1 = query.ToList();
6.构造函数
用过 EntityFramework 的同学都知道,如果要通过构造函数的方式查询指定字段,除非老老实实重新定义一个新的实体,否则一个 “The entity or complex type cannot be constructed in a LINQ to Entities query“ 的异常马上给甩你脸上。XFramework 框架的这个用法,就是为了让你远离这会呼吸的痛!~
1 // 构造函数 2 var query = 3 from a in context.GetTable<Model.Demo>() 4 where a.DemoId <= 10 5 select new Model.Demo(a); 6 var r1 = query.ToList(); 7 //SQL=> 8 //SELECT 9 //t0.[DemoId] AS [DemoId], 10 //t0.[DemoCode] AS [DemoCode], 11 //t0.[DemoName] AS [DemoName], 12 //... 13 //FROM [Sys_Demo] t0 14 //WHERE t0.[DemoId] <= 10 15 query = 16 from a in context.GetTable<Model.Demo>() 17 where a.DemoId <= 10 18 select new Model.Demo(a.DemoId, a.DemoName); 19 r1 = query.ToList();
7. 分页查询
1 // 分页查询 2 // 1.不是查询第一页的内容时,必须先OrderBy再分页,OFFSET ... Fetch Next 分页语句要求有 OrderBy 3 // 2.OrderBy表达式里边的参数必须跟query里边的变量名一致,如此例里的 a。SQL解析时根据此变更生成表别名 4 query = from a in context.GetTable<TDemo>() 5 orderby a.DemoCode 6 select a; 7 query = query.Skip(1).Take(18); 8 result1 = query.ToList(); 9 // 点标记 10 query = context 11 .GetTable<TDemo>() 12 .OrderBy(a => a.DemoCode) 13 .Skip(1) 14 .Take(18); 15 result1 = query.ToList();
8. 过滤条件
1 // 过滤条件 2 query = from a in context.GetTable<TDemo>() 3 where a.DemoName == "D0000002" || a.DemoCode == "D0000002" 4 select a; 5 result1 = query.ToList(); 6 // 点标记 7 query = context.GetTable<TDemo>().Where(a => a.DemoName == "D0000002" || a.DemoCode == "D0000002"); 8 result1 = query.ToList(); 9 query = context.GetTable<TDemo>().Where(a => a.DemoName.Contains("004")); 10 result1 = query.ToList(); 11 query = context.GetTable<TDemo>().Where(a => a.DemoCode.StartsWith("Code000036")); 12 result1 = query.ToList(); 13 query = context.GetTable<TDemo>().Where(a => a.DemoCode.EndsWith("004")); 14 result1 = query.ToList();
9. 更多条件
1 // 支持的查询条件 2 // 区分 nvarchar,varchar,date,datetime,datetime2 字段类型 3 // 支持的字符串操作=> Trim | TrimStart | TrimEnd | ToString | Length 4 int m_byte = 9; 5 Model.State state = Model.State.Complete; 6 query = from a in context.GetTable<TDemo>() 7 where 8 a.DemoCode == "002" && 9 a.DemoName == "002" && 10 a.DemoCode.Contains("TAN") && // LIKE '%%' 11 a.DemoName.Contains("TAN") && // LIKE '%%' 12 a.DemoCode.StartsWith("TAN") && // LIKE 'K%' 13 a.DemoCode.EndsWith("TAN") && // LIKE '%K' 14 a.DemoCode.Length == 12 && // LENGTH 15 a.DemoCode.TrimStart() == "TF" && 16 a.DemoCode.TrimEnd() == "TF" && 17 a.DemoCode.TrimEnd() == "TF" && 18 a.DemoCode.Substring(0) == "TF" && 19 a.DemoDate == DateTime.Now && 20 a.DemoDateTime == DateTime.Now && 21 a.DemoDateTime2 == DateTime.Now && 22 a.DemoName == ( 23 a.DemoDateTime_Nullable == null ? "NULL" : "NOT NULL") && // 三元表达式 24 a.DemoName == (a.DemoName ?? a.DemoCode) && // 二元表达式 25 new[] { 1, 2, 3 }.Contains(a.DemoId) && // IN(1,2,3) 26 new List<int> { 1, 2, 3 }.Contains(a.DemoId) && // IN(1,2,3) 27 new List<int>(_demoIdList).Contains(a.DemoId) && // IN(1,2,3) 28 a.DemoId == new List<int> { 1, 2, 3 }[0] && // IN(1,2,3) 29 _demoIdList.Contains(a.DemoId) && // IN(1,2,3) 30 a.DemoName == _demoName && 31 a.DemoCode == (a.DemoCode ?? "CODE") && 32 new List<string> { "A", "B", "C" }.Contains(a.DemoCode) && 33 a.DemoByte == (byte)m_byte && 34 a.DemoByte == (byte)Model.State.Complete || 35 a.DemoInt == (int)Model.State.Complete || 36 a.DemoInt == (int)state || 37 (a.DemoName == "STATE" && a.DemoName == "REMARK")// OR 查询 38 select a; 39 result1 = query.ToList();
10. DataTable和DataSet
1 // DataTable 2 query = from a in context.GetTable<TDemo>() 3 orderby a.DemoCode 4 select a; 5 query = query.Take(18); 6 var result3 = context.Database.ExecuteDataTable(query); 7 8 // DataSet 9 var define = query.Resolve(); 10 List<DbCommandDefinition> sqlList = new List<DbCommandDefinition> { define, define, define }; 11 var result4 = context.Database.ExecuteDataSet(sqlList);
11. 内联查询
1 // INNER JOIN 2 var query = 3 from a in context.GetTable<Model.Client>() 4 join b in context.GetTable<Model.CloudServer>() on a.CloudServerId equals b.CloudServerId 5 where a.ClientId > 0 6 select a; 7 var result = query.ToList(); 8 // 点标记 9 query = context 10 .GetTable<Model.Client>() 11 .Join(context.GetTable<Model.CloudServer>(), a => a.CloudServerId, b => b.CloudServerId, (a, b) => a) 12 .Where(a => a.ClientId > 0); 13 result = query.ToList();
12. 左联查询
注意看第二个左关联,使用常量作为关联键,翻译出来的SQL语句大概是这样的:
SELECT ***
FROM [Bas_Client] t0
LEFT JOIN [Sys_CloudServer] t1 ON t0.[CloudServerId] = t1.[CloudServerId] AND N'567' = t1.[CloudServerCode]
WHERE t1.[CloudServerName] IS NOT NULL
有没有看到熟悉的味道,兄dei?
1 // LEFT JOIN 2 query = 3 from a in context.GetTable<Model.Client>() 4 join b in context.GetTable<Model.CloudServer>() on a.CloudServerId equals b.CloudServerId into u_b 5 from b in u_b.DefaultIfEmpty() 6 select a; 7 query = query.Where(a => a.CloudServer.CloudServerName != null); 8 result = query.ToList(); 9 10 // LEFT JOIN 11 query = 12 from a in context.GetTable<Model.Client>() 13 join b in context.GetTable<Model.CloudServer>() on new { a.CloudServerId, CloudServerCode = "567" } equals new { b.CloudServerId, b.CloudServerCode } into u_b 14 from b in u_b.DefaultIfEmpty() 15 select a; 16 query = query.Where(a => a.CloudServer.CloudServerName != null); 17 result = query.ToList();
13. 右联查询
左关联和右关联的语法我这里用的是一样的,不过是 DefaultIfEmpty 方法加多了一个重载,DefaultIfEmpty(true) 即表示右关联。
1 // RIGHT JOIN 2 query = 3 from a in context.GetTable<Model.CloudServer>() 4 join b in context.GetTable<Model.Client>() on a.CloudServerId equals b.CloudServerId into u_b 5 from b in u_b.DefaultIfEmpty(true) 6 where a.CloudServerName == null 7 select b; 8 result = query.ToList();
14. Union查询
我们的Union查询支持 UNION 操作后再分页哦~
1 // UNION 注意UNION分页的写法,仅支持写在最后 2 var q1 = context.GetTable<Model.Client>().Where(x => x.ClientId == 0); 3 var q2 = context.GetTable<Model.Client>().Where(x => x.ClientId == 0); 4 var q3 = context.GetTable<Model.Client>().Where(x => x.ClientId == 0); 5 var query6 = q1.Union(q2).Union(q3); 6 var result6 = query6.ToList(); 7 result6 = query6.Take(2).ToList(); 8 result6 = query6.OrderBy(a => a.ClientId).Skip(2).ToList(); 9 query6 = query6.Take(2); 10 result6 = query6.ToList(); 11 query6 = query6.OrderBy(a => a.ClientId).Skip(1).Take(2); 12 result6 = query6.ToList();
15. 导航属性
1 // 更简单的赋值方式 2 // 适用场景:在显示列表时只想显示外键表的一两个字段 3 query = 4 from a in context.GetTable<Model.Client>() 5 select new Model.Client(a) 6 { 7 CloudServer = a.CloudServer, 8 LocalServer = new Model.CloudServer 9 { 10 CloudServerId = a.CloudServerId, 11 CloudServerName = a.LocalServer.CloudServerName 12 } 13 }; 14 result = query.ToList();
16. 一对一一对多导航
1 // 1:1关系,1:n关系 2 query = 3 from a in context.GetTable<Model.Client>() 4 where a.ClientId > 0 5 orderby a.ClientId 6 select new Model.Client(a) 7 { 8 CloudServer = a.CloudServer, 9 Accounts = a.Accounts 10 }; 11 result = query.ToList();
17. Include 语法
EntityFramework 有Include语法,咱也有,而且是实打实的一次性加载!!!
1 // Include 语法 2 query = 3 context 4 .GetTable<Model.Client>() 5 .Include(a => a.CloudServer); 6 --query = 7 -- from a in query 8 -- join b in context.GetTable<Model.CloudServer>() on a.CloudServerId equals b.CloudServerId 9 -- orderby a.ClientId 10 -- select new Model.Client(a) 11 -- { 12 -- CloudServer = a.CloudServer 13 -- }; 14 result = query.ToList(); 15 16 // 还是Include,无限主从孙 ### 17 query = 18 from a in context 19 .GetTable<Model.Client>() 20 .Include(a => a.Accounts) 21 .Include(a => a.Accounts[0].Markets) 22 .Include(a => a.Accounts[0].Markets[0].Client) 23 where a.ClientId > 0 24 orderby a.ClientId 25 select a; 26 result = query.ToList(); 27 28 // Include 分页 29 query = 30 from a in context 31 .GetTable<Model.Client>() 32 .Include(a => a.Accounts) 33 .Include(a => a.Accounts[0].Markets) 34 .Include(a => a.Accounts[0].Markets[0].Client) 35 where a.ClientId > 0 36 orderby a.ClientId 37 select a; 38 query = query 39 .Where(a => a.ClientId > 0 && a.CloudServer.CloudServerId > 0) 40 .Skip(10) 41 .Take(20); 42 result = query.ToList(); 43 query = 44 from a in context 45 .GetTable<Model.Client>() 46 .Include(a => a.CloudServer) 47 .Include(a => a.Accounts) 48 where a.ClientId > 0 49 select a; 50 query = query.OrderBy(a => a.ClientId); 51 result = query.ToList(); 52 53 // Include 语法查询 主 从 孙 关系<注:相同的导航属性不能同时用include和join> 54 var query1 = 55 from a in 56 context 57 .GetTable<Model.Client>() 58 .Include(a => a.CloudServer) 59 .Include(a => a.Accounts) 60 .Include(a => a.Accounts[0].Markets) 61 .Include(a => a.Accounts[0].Markets[0].Client) 62 group a by new { a.ClientId, a.ClientCode, a.ClientName, a.CloudServer.CloudServerId } into g 63 select new Model.Client 64 { 65 ClientId = g.Key.ClientId, 66 ClientCode = g.Key.ClientCode, 67 ClientName = g.Key.ClientName, 68 CloudServerId = g.Key.CloudServerId, 69 Qty = g.Sum(a => a.Qty) 70 }; 71 query1 = query1 72 .Where(a => a.ClientId > 0) 73 .OrderBy(a => a.ClientId) 74 .Skip(10) 75 .Take(20) 76 ; 77 var result1 = query1.ToList();
18. 分组查询
1 var query2 = 2 from a in context.GetTable<Model.Client>() 3 group a by a.ClientId into g 4 select new 5 { 6 ClientId = g.Key, 7 Qty = g.Sum(a => a.Qty) 8 }; 9 query2 = query2.OrderBy(a => a.ClientId).ThenBy(a => a.Qty);
19. 聚合函数
1 var result1 = query2.Max(a => a.ClientId); 2 var result2 = query2.Sum(a => a.Qty); 3 var result3 = query2.Min(a => a.ClientId); 4 var result4= query2.Average(a => a.Qty); 5 var result5 = query2.Count();
20. 分组分页
1 // 分组后再分页 2 var query8 = 3 from a in context.GetTable<Model.Client>() 4 where a.ClientName == "TAN" 5 group a by new { a.ClientId, a.ClientName } into g 6 where g.Key.ClientId > 0 7 orderby new { g.Key.ClientName, g.Key.ClientId } 8 select new 9 { 10 Id = g.Key.ClientId, 11 Name = g.Min(a => a.ClientId) 12 }; 13 query8 = query8.Skip(2).Take(3); 14 var result8 = query8.ToList();
21. 子查询
1 // 强制转为子查询 2 query = 3 from a in context.GetTable<Model.Client>() 4 join b in context.GetTable<Model.CloudServer>() on a.CloudServerId equals b.CloudServerId into u_c 5 from b in u_c.DefaultIfEmpty() 6 select a; 7 query = query.OrderBy(a => a.ClientId).Skip(10).Take(10).AsSubQuery(); 8 query = from a in query 9 join b in context.GetTable<Model.Client>() on a.ClientId equals b.ClientId 10 select a; 11 result = query.ToList();
22. Any 查询
1 // Any 2 var isAny = context.GetTable<Model.Client>().Any(); 3 isAny = context.GetTable<Model.Client>().Any(a => a.ActiveDate == DateTime.Now); 4 isAny = context.GetTable<Model.Client>().Distinct().Any(a => a.ActiveDate == DateTime.Now); 5 isAny = context.GetTable<Model.Client>().OrderBy(a => a.ClientId).Skip(2).Take(5).Any(a => a.ActiveDate == DateTime.Now); 6 //SQL=> 7 //IF EXISTS( 8 // SELECT TOP 1 1 9 // FROM[Bas_Client] t0 10 // WHERE t0.[ActiveDate] = '2018-08-15 14:07:09.784' 11 //) SELECT 1 ELSE SELECT 0
23. 单个删除
1 // 1. 删除单个记录 2 var demo = new TDemo { DemoId = 1 }; 3 context.Delete(demo); 4 context.SubmitChanges();
24. 批量删除
1 // 2.WHERE 条件批量删除 2 context.Delete<TDemo>(a => a.DemoId == 2 || a.DemoId == 3 || a.DemoName == "N0000004"); 3 var qeury = 4 context 5 .GetTable<TDemo>() 6 .Where(a => a.DemoId == 2 || a.DemoId == 3 || a.DemoName == "N0000004"); 7 // 2.WHERE 条件批量删除 8 context.Delete<TDemo>(qeury); 9 context.SubmitChanges();
25. 多表关联删除
1 // 3.Query 关联批量删除 2 var query1 = 3 from a in context.GetTable<Model.Client>() 4 join b in context.GetTable<Model.ClientAccount>() on a.ClientId equals b.ClientId 5 join c in context.GetTable<Model.ClientAccountMarket>() on new { b.ClientId, b.AccountId } equals new { c.ClientId, c.AccountId } 6 where c.ClientId == 5 && c.AccountId == "1" && c.MarketId == 1 7 select a; 8 context.Delete<Model.Client>(query1); 9 10 // oracle 不支持导航属性关联删除 11 // 3.Query 关联批量删除 12 var query2 = 13 from a in context.GetTable<Model.Client>() 14 join b in context.GetTable<Model.ClientAccount>() on a.ClientId equals b.ClientId 15 where a.CloudServer.CloudServerId == 20 && a.LocalServer.CloudServerId == 2 16 select a; 17 context.Delete<Model.Client>(query2); 18 // 4.Query 关联批量删除 19 var query3 = 20 from a in context.GetTable<Model.Client>() 21 where a.CloudServer.CloudServerId == 20 && a.LocalServer.CloudServerId == 2 22 select a; 23 context.Delete<Model.Client>(query3); 24 25 26 // 5.子查询批量删除 27 // 子查询更新 28 var sum = 29 from a in context.GetTable<Model.ClientAccount>() 30 where a.ClientId <= 20 31 group a by new { a.ClientId } into g 32 select new Model.Client 33 { 34 ClientId = g.Key.ClientId, 35 Qty = g.Sum(a => a.Qty) 36 }; 37 var query4 = 38 from a in context.GetTable<Model.Client>() 39 join b in context.GetTable<Model.CloudServer>() on a.CloudServerId equals b.CloudServerId 40 join c in context.GetTable<Model.CloudServer>() on a.CloudServerId equals c.CloudServerId 41 join d in sum on a.ClientId equals d.ClientId 42 where a.ClientId > 10 && a.CloudServerId < 0 43 select a; 44 context.Delete<Model.Client>(query4);
26. 单个更新
1 var demo = context 2 .GetTable<TDemo>() 3 .FirstOrDefault(x => x.DemoId > 0); 4 5 // 整个实体更新 6 demo.DemoName = "001'.N"; 7 context.Update(demo); 8 context.SubmitChanges();
27.批量更新
1 // 2.WHERE 条件批量更新 2 context.Update<TDemo>(x => new TDemo 3 { 4 DemoDateTime2 = DateTime.UtcNow, 5 DemoDateTime2_Nullable = null, 6 //DemoTime_Nullable = ts 7 }, x => x.DemoName == "001'.N" || x.DemoCode == "001'.N"); 8 context.SubmitChanges();
28. 多表关联更新
这里还支持将B表字段的值更新回A表,有多方便你自己体会。事先声明,Oracle和Postgre是不支持这种sao操作的。
1 // 3.Query 关联批量更新 2 var query = 3 from a in context.GetTable<Model.Client>() 4 where a.CloudServer.CloudServerId != 0 5 select a; 6 context.Update<Model.Client>(a => new Model.Client 7 { 8 Remark = "001.TAN" 9 }, query); 10 11 // 更新本表值等于从表的字段值 12 query = 13 from a in context.GetTable<Model.Client>() 14 join b in context.GetTable<Model.CloudServer>() on a.CloudServerId equals b.CloudServerId 15 join c in context.GetTable<Model.ClientAccount>() on a.ClientId equals c.ClientId 16 where c.AccountId == "12" 17 select a; 18 context.Update<Model.Client, Model.CloudServer>((a, b) => new Model.Client 19 { 20 CloudServerId = b.CloudServerId, 21 Remark = "001.TAN" 22 }, query); 23 context.SubmitChanges();
29. 子查询更新
1 // 子查询更新 2 var sum = 3 from a in context.GetTable<Model.ClientAccount>() 4 where a.ClientId > 0 5 group a by new { a.ClientId } into g 6 select new Model.Client 7 { 8 ClientId = g.Key.ClientId, 9 Qty = g.Sum(a => a.Qty) 10 }; 11 if (_databaseType == DatabaseType.SqlServer || _databaseType == DatabaseType.MySql) 12 { 13 var uQuery = 14 from a in context.GetTable<Model.Client>() 15 join b in sum on a.ClientId equals b.ClientId 16 where a.ClientId > 0 && b.ClientId > 0 17 select a; 18 context.Update<Model.Client, Model.Client>((a, b) => new Model.Client { Qty = b.Qty }, uQuery); 19 } 20 else 21 { 22 // npg oracle 翻译成 EXISTS,更新字段的值不支持来自子查询 23 var uQuery = 24 from a in context.GetTable<Model.Client>() 25 join b in sum on a.ClientId equals b.ClientId 26 where a.ClientId > 0 // b.ClientId > 0 27 select a; 28 context.Update<Model.Client>(a => new Model.Client { Qty = 9 }, uQuery); 29 } 30 context.SubmitChanges();
30. 带自增列新增
1 // 带自增列 2 var demo = new TDemo 3 { 4 DemoCode = "D0000001", 5 DemoName = "N0000001", 6 DemoBoolean = true, 7 DemoChar = 'A', 8 DemoNChar = 'B', 9 DemoByte = 64, 10 DemoDate = DateTime.Now, 11 DemoDateTime = DateTime.Now, 12 DemoDateTime2 = DateTime.Now, 13 DemoDecimal = 64, 14 DemoDouble = 64, 15 DemoFloat = 64, 16 DemoGuid = Guid.NewGuid(), 17 DemoShort = 64, 18 DemoInt = 64, 19 DemoLong = 64 20 }; 21 context.Insert(demo); 22 context.SubmitChanges(); 23 24 var demo2 = new TDemo 25 { 26 DemoCode = "D0000002", 27 DemoName = "N0000002", 28 DemoBoolean = true, 29 DemoChar = 'A', 30 DemoNChar = 'B', 31 DemoByte = 65, 32 DemoDate = DateTime.Now, 33 DemoDateTime = DateTime.Now, 34 DemoDateTime2 = DateTime.Now, 35 DemoDecimal = 65, 36 DemoDouble = 65, 37 DemoFloat = 65, 38 DemoGuid = Guid.NewGuid(), 39 DemoShort = 65, 40 DemoInt = 65, 41 DemoLong = 65 42 }; 43 context.Insert(demo2); 44 45 var demo3 = new TDemo 46 { 47 DemoCode = "D0000003", 48 DemoName = "N0000003", 49 DemoBoolean = true, 50 DemoChar = 'A', 51 DemoNChar = 'B', 52 DemoByte = 66, 53 DemoDate = DateTime.Now, 54 DemoDateTime = DateTime.Now, 55 DemoDateTime2 = DateTime.Now, 56 DemoDecimal = 66, 57 DemoDouble = 66, 58 DemoFloat = 66, 59 DemoGuid = Guid.NewGuid(), 60 DemoShort = 66, 61 DemoInt = 66, 62 DemoLong = 66 63 }; 64 context.Insert(demo3); 65 context.Insert(demo); 66 context.SubmitChanges();
31. 批量新增
批量新增翻译的SQL不带参数,只是纯SQL文本。SQLSERVER的同学如果想更快,可以尝尝 SqlDbContext.BulkCopy方法。
1 // 批量增加 2 // 产生 INSERT INTO VALUES(),(),()... 语法。注意这种批量增加的方法并不能给自增列自动赋值 3 context.Delete<TDemo>(x => x.DemoId > 1000000); 4 demos = new List<TDemo>(); 5 for (int i = 0; i < 1002; i++) 6 { 7 TDemo d = new TDemo 8 { 9 DemoCode = "D0000001", 10 DemoName = "N0000001", 11 DemoBoolean = true, 12 DemoChar = 'A', 13 DemoNChar = 'B', 14 DemoByte = 64, 15 DemoDate = DateTime.Now, 16 DemoDateTime = DateTime.Now, 17 DemoDateTime2 = DateTime.Now, 18 DemoDecimal = 64, 19 DemoDouble = 64, 20 DemoFloat = 64, 21 DemoGuid = Guid.NewGuid(), 22 DemoShort = 64, 23 DemoInt = 64, 24 DemoLong = 64 25 }; 26 demos.Add(d); 27 } 28 context.Insert<TDemo>(demos); 29 context.SubmitChanges();
32. 关联查询新增
1 // 子查询增 2 var sum = 3 from a in context.GetTable<Model.ClientAccount>() 4 where a.ClientId > 0 5 group a by new { a.ClientId } into g 6 select new Model.Client 7 { 8 ClientId = g.Key.ClientId, 9 Qty = g.Sum(a => a.Qty) 10 }; 11 sum = sum.AsSubQuery(); 12 13 maxId = context.GetTable<Model.Client>().Max(x => x.ClientId); 14 nextId = maxId + 1; 15 var nQuery = 16 from a in sum 17 join b in context.GetTable<Model.Client>() on a.ClientId equals b.ClientId into u_b 18 from b in u_b.DefaultIfEmpty() 19 where b.ClientId == null 20 select new Model.Client 21 { 22 ClientId = SqlMethod.RowNumber<int>(x => a.ClientId) + nextId, 23 ClientCode = "ABC3", 24 ClientName = "啊啵呲3", 25 CloudServerId = 11, 26 State = 3, 27 Qty = a.Qty, 28 }; 29 context.Insert(nQuery);
33. 增删改同时查出数据
1 context.Update<Model.Client>(x => new Model.Client 2 { 3 ClientName = "蒙3" 4 }, x => x.ClientId == 3); 5 var query = 6 from a in context.GetTable<Model.Client>() 7 where a.ClientId == 1 8 select 5; 9 context.AddQuery(query); 10 List<int> result1 = null; 11 context.SubmitChanges(out result1); 12 13 context.Update<Model.Client>(x => new Model.Client 14 { 15 ClientName = "蒙4" 16 }, x => x.ClientId == 4); 17 query = 18 from a in context.GetTable<Model.Client>() 19 where a.ClientId == 1 20 select 5; 21 context.AddQuery(query); 22 var query2 = 23 from a in context.GetTable<Model.Client>() 24 where a.ClientId == 1 25 select 6; 26 context.AddQuery(query2); 27 result1 = null; 28 List<int> result2 = null; 29 context.SubmitChanges(out result1, out result2);
34. 一次性加载多个列表
1 // 一性加载多个列表 **** 2 var query3 = 3 from a in context.GetTable<Model.Client>() 4 where a.ClientId >= 1 && a.ClientId <= 10 5 select 5; 6 var query4 = 7 from a in context.GetTable<Model.Client>() 8 where a.ClientId >= 1 && a.ClientId <= 10 9 select 6; 10 var tuple = context.Database.ExecuteMultiple<int, int>(query3, query4); 11 12 query3 = 13 from a in context.GetTable<Model.Client>() 14 where a.ClientId >= 1 && a.ClientId <= 10 15 select 5; 16 query4 = 17 from a in context.GetTable<Model.Client>() 18 where a.ClientId >= 1 && a.ClientId <= 10 19 select 6; 20 var query5 = 21 from a in context.GetTable<Model.Client>() 22 where a.ClientId >= 1 && a.ClientId <= 10 23 select 7; 24 var tuple2 = context.Database.ExecuteMultiple<int, int, int>(query3, query4, query5);
35. 事务操作
借鉴 EntityFramework的思想,本框架也支持自身开启事务,或者从其它上下文开启事务后再在本框架使用该事务。
// 事务1. 上下文独立事务 try { using (var transaction = context.Database.BeginTransaction()) { var result = context.GetTable<Model.Client>().FirstOrDefault(x => x.ClientId <= 10); context.Update<Model.Client>(x => new Model.Client { ClientName = "事务1" }, x => x.ClientId == result.ClientId); context.SubmitChanges(); result = context.GetTable<Model.Client>().FirstOrDefault(x => x.ClientId == result.ClientId); context.Update<Model.Client>(x => new Model.Client { ClientName = "事务2" }, x => x.ClientId == result.ClientId); context.SubmitChanges(); result = context.GetTable<Model.Client>().FirstOrDefault(x => x.ClientId == result.ClientId); //throw new Exception("假装异常"); //transaction.Rollback(); transaction.Commit(); } } finally { // 开启事务后必需显式释放资源 context.Dispose(); } // 事务2. 使用其它的事务 IDbTransaction transaction2 = null; IDbConnection connection = null; try { connection = context.Database.DbProviderFactory.CreateConnection(); connection.ConnectionString = context.Database.ConnectionString; if (connection.State != ConnectionState.Open) connection.Open(); transaction2 = connection.BeginTransaction(); // 指定事务 context.Database.Transaction = transaction2; var result = context.GetTable<Model.Client>().FirstOrDefault(x => x.ClientId <= 10); context.Update<Model.Client>(x => new Model.Client { ClientName = "事务3" }, x => x.ClientId == result.ClientId); context.SubmitChanges(); result = context.GetTable<Model.Client>().FirstOrDefault(x => x.ClientId == result.ClientId); context.Update<Model.Client>(x => new Model.Client { ClientName = "事务4" }, x => x.ClientId == result.ClientId); result = context.GetTable<Model.Client>().FirstOrDefault(x => x.ClientId == result.ClientId); string sql = @"UPDATE Bas_Client SET ClientName = N'事务5' WHERE ClientID=2;UPDATE Bas_Client SET ClientName = N'事务6' WHERE ClientID=3;"; context.AddQuery(sql); context.SubmitChanges(); transaction2.Commit(); } catch { if (transaction2 != null) transaction2.Rollback(); throw; } finally { if (transaction2 != null) transaction2.Dispose(); if (connection != null) connection.Close(); if (connection != null) connection.Dispose(); context.Dispose(); }
36. 表变量
SQLSERVER的童鞋看过来,你要的爽本框架都能给~
1 // 声明表变量 2 var typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo<SqlServerModel.JoinKey>(); 3 context.AddQuery(string.Format("DECLARE {0} [{1}]", typeRuntime.TableName, typeRuntime.TableName.TrimStart('@'))); 4 List<SqlServerModel.JoinKey> keys = new List<SqlServerModel.JoinKey> 5 { 6 new SqlServerModel.JoinKey{ Key1 = 2 }, 7 new SqlServerModel.JoinKey{ Key1 = 3 }, 8 }; 9 // 向表变量写入数据 10 context.Insert<SqlServerModel.JoinKey>(keys); 11 // 像物理表一样操作表变量 12 var query = 13 from a in context.GetTable<Model.Client>() 14 join b in context.GetTable<SqlServerModel.JoinKey>() on a.ClientId equals b.Key1 15 select a; 16 context.AddQuery(query); 17 // 提交查询结果 18 List<Model.Client> result = null; 19 context.SubmitChanges(out result);
【结语】
XFramework 已成熟运用于我们公司的多个核心项目,完全代替了之前的 Dapper + DbHelper的数据持久方案。从最初只支持SQLSERVER到支持MySQL、Postgre和Oracle,一个多月的熬夜坚持,个中酸爽只有经历过才能体会。你的喜爱和支持是我在开源的路上一路狂奔的最大动力,撸码不易,不喜请轻喷。但我相信,这绝对是一款人性化、有温度的数据持久框架!!!
XFramework 现已完全开源,遵循 Apache2.0 协议,托管地址:
码云:https://gitee.com/TANZAME/Riz.XFramework
GitHub:https://github.com/TANZAME/Riz.XFramework
技术交流群:816425449