[开源].NET高性能框架Chloe.ORM-完美支持SQLite,chloe.orm-sqlite
扯淡
这是一款轻量、高效的.NET C#数据库访问框架(ORM)。查询接口借鉴 Linq(但不支持 Linq)。借助 lambda 表达式,可以完全用面向对象的方式就能轻松执行多表连接查询、分组查询、聚合查询、插入数据、批量删除和更新等操作。
多数据库支持,已然成为 ORM 的标配。继开发了 Chloe 的 .NET Core 版本后,Chloe 继续她的多数据库支持之路。上周花了些时间学习了 SQLite,现在已经支持 SQLite 数据库。
导航
- Chloe.ORM
- 事前准备
- 查询数据
- 基本查询
- 连接查询
- 聚合查询
- 分组查询
- 插入数据
- 更新数据
- 删除数据
- 支持函数
- 坎坎坷坷
- 结语
Chloe.ORM
事前准备
实体:

因为框架不依赖具体的数据库驱动,所以得先建个 SQLiteConnectionFactory 类,实现 IDbConnectionFactory 接口:
public class SQLiteConnectionFactory : IDbConnectionFactory { string _connString = null; public SQLiteConnectionFactory(string connString) { this._connString = connString; } public IDbConnection CreateConnection() { SQLiteConnection conn = new SQLiteConnection(this._connString); return conn; } }
然后通过构造函数注入的方式创建一个 DbContext:
SQLiteContext context = new SQLiteContext(new SQLiteConnectionFactory(DbHelper.ConnectionString));
再创建一个 IQuery<T>:
IQuery<User> q = context.Query<User>();
查询数据
基本查询
IQuery<User> q = context.Query<User>(); q.Where(a => a.Id == 1).FirstOrDefault(); /* * SELECT [Users].[Id] AS [Id],[Users].[Name] AS [Name],[Users].[Gender] AS [Gender],[Users].[Age] AS [Age],[Users].[CityId] AS [CityId],[Users].[OpTime] AS [OpTime] FROM [Users] AS [Users] WHERE [Users].[Id] = 1 LIMIT 1 OFFSET 0 */ //可以选取指定的字段 q.Where(a => a.Id == 1).Select(a => new { a.Id, a.Name }).FirstOrDefault(); /* * SELECT [Users].[Id] AS [Id],[Users].[Name] AS [Name] FROM [Users] AS [Users] WHERE [Users].[Id] = 1 LIMIT 1 OFFSET 0 */ //分页 q.Where(a => a.Id > 0).OrderBy(a => a.Age).Skip(1).Take(999).ToList(); /* * SELECT [Users].[Id] AS [Id],[Users].[Name] AS [Name],[Users].[Gender] AS [Gender],[Users].[Age] AS [Age],[Users].[CityId] AS [CityId],[Users].[OpTime] AS [OpTime] FROM [Users] AS [Users] WHERE [Users].[Id] > 0 ORDER BY [Users].[Age] ASC LIMIT 999 OFFSET 1 */
连接查询
IQuery<User> users = context.Query<User>(); IQuery<City> cities = context.Query<City>(); IQuery<Province> provinces = context.Query<Province>(); //建立连接 IJoiningQuery<User, City> user_city = users.InnerJoin(cities, (user, city) => user.CityId == city.Id); IJoiningQuery<User, City, Province> user_city_province = user_city.InnerJoin(provinces, (user, city, province) => city.ProvinceId == province.Id); //查出一个用户及其隶属的城市和省份的所有信息 user_city_province.Select((user, city, province) => new { User = user, City = city, Province = province }).Where(a => a.User.Id == 1).ToList(); /* * SELECT [Users].[Id] AS [Id],[Users].[Name] AS [Name],[Users].[Gender] AS [Gender],[Users].[Age] AS [Age],[Users].[CityId] AS [CityId],[Users].[OpTime] AS [OpTime],[City].[Id] AS [Id0],[City].[Name] AS [Name0],[City].[ProvinceId] AS [ProvinceId],[Province].[Id] AS [Id1],[Province].[Name] AS [Name1] FROM [Users] AS [Users] INNER JOIN [City] AS [City] ON [Users].[CityId] = [City].[Id] INNER JOIN [Province] AS [Province] ON [City].[ProvinceId] = [Province].[Id] WHERE [Users].[Id] = 1 */ //也可以只获取指定的字段信息:UserId,UserName,CityName,ProvinceName user_city_province.Select((user, city, province) => new { UserId = user.Id, UserName = user.Name, CityName = city.Name, ProvinceName = province.Name }).Where(a => a.UserId == 1).ToList(); /* * SELECT [Users].[Id] AS [UserId],[Users].[Name] AS [UserName],[City].[Name] AS [CityName],[Province].[Name] AS [ProvinceName] FROM [Users] AS [Users] INNER JOIN [City] AS [City] ON [Users].[CityId] = [City].[Id] INNER JOIN [Province] AS [Province] ON [City].[ProvinceId] = [Province].[Id] WHERE [Users].[Id] = 1 */
聚合查询
IQuery<User> q = context.Query<User>(); q.Select(a => AggregateFunctions.Count()).First(); /* * SELECT COUNT(1) AS [C] FROM [Users] AS [Users] LIMIT 1 OFFSET 0 */ q.Select(a => new { Count = AggregateFunctions.Count(), LongCount = AggregateFunctions.LongCount(), Sum = AggregateFunctions.Sum(a.Age), Max = AggregateFunctions.Max(a.Age), Min = AggregateFunctions.Min(a.Age), Average = AggregateFunctions.Average(a.Age) }).First(); /* * SELECT COUNT(1) AS [Count],COUNT(1) AS [LongCount],CAST(SUM([Users].[Age]) AS INTEGER) AS [Sum],CAST(MAX([Users].[Age]) AS INTEGER) AS [Max],CAST(MIN([Users].[Age]) AS INTEGER) AS [Min],CAST(AVG([Users].[Age]) AS REAL) AS [Average] FROM [Users] AS [Users] LIMIT 1 OFFSET 0 */ var count = q.Count(); /* * SELECT COUNT(1) AS [C] FROM [Users] AS [Users] */ var longCount = q.LongCount(); /* * SELECT COUNT(1) AS [C] FROM [Users] AS [Users] */ var sum = q.Sum(a => a.Age); /* * SELECT CAST(SUM([Users].[Age]) AS INTEGER) AS [C] FROM [Users] AS [Users] */ var max = q.Max(a => a.Age); /* * SELECT CAST(MAX([Users].[Age]) AS INTEGER) AS [C] FROM [Users] AS [Users] */ var min = q.Min(a => a.Age); /* * SELECT CAST(MIN([Users].[Age]) AS INTEGER) AS [C] FROM [Users] AS [Users] */ var avg = q.Average(a => a.Age); /* * SELECT CAST(AVG([Users].[Age]) AS REAL) AS [C] FROM [Users] AS [Users] */
分组查询
IQuery<User> q = context.Query<User>(); IGroupingQuery<User> g = q.Where(a => a.Id > 0).GroupBy(a => a.Age); g = g.Having(a => a.Age > 1 && AggregateFunctions.Count() > 0); g.Select(a => new { a.Age, Count = AggregateFunctions.Count(), Sum = AggregateFunctions.Sum(a.Age), Max = AggregateFunctions.Max(a.Age), Min = AggregateFunctions.Min(a.Age), Avg = AggregateFunctions.Average(a.Age) }).ToList(); /* * SELECT [Users].[Age] AS [Age],COUNT(1) AS [Count],CAST(SUM([Users].[Age]) AS INTEGER) AS [Sum],CAST(MAX([Users].[Age]) AS INTEGER) AS [Max],CAST(MIN([Users].[Age]) AS INTEGER) AS [Min],CAST(AVG([Users].[Age]) AS REAL) AS [Avg] FROM [Users] AS [Users] WHERE [Users].[Id] > 0 GROUP BY [Users].[Age] HAVING ([Users].[Age] > 1 AND COUNT(1) > 0) */
插入数据
方式1
以 lambda 表达式树的方式插入:
//返回主键 Id int id = (int)context.Insert<User>(() => new User() { Name = "lu", Age = 18, Gender = Gender.Man, CityId = 1, OpTime = DateTime.Now }); /* * INSERT INTO [Users]([Name],[Age],[Gender],[CityId],[OpTime]) VALUES('lu',18,1,1,DATETIME('NOW','LOCALTIME'));SELECT LAST_INSERT_ROWID() */
方式2
以实体的方式插入:
User user = new User(); user.Name = "lu"; user.Age = 18; user.Gender = Gender.Man; user.CityId = 1; user.OpTime = DateTime.Now; //会自动将自增 Id 设置到 user 的 Id 属性上 user = context.Insert(user); /* * String @P_0 = 'lu'; Gender @P_1 = Man; Int32 @P_2 = 18; Int32 @P_3 = 1; DateTime @P_4 = '2016/8/6 22:03:42'; INSERT INTO [Users]([Name],[Gender],[Age],[CityId],[OpTime]) VALUES(@P_0,@P_1,@P_2,@P_3,@P_4);SELECT LAST_INSERT_ROWID() */
更新数据
方式1
以 lambda 表达式树的方式更新:
context.Update<User>(a => new User() { Name = a.Name, Age = a.Age + 100, Gender = Gender.Man, OpTime = DateTime.Now }, a => a.Id == 1); /* * UPDATE [Users] SET [Name]=[Users].[Name],[Age]=([Users].[Age] + 100),[Gender]=1,[OpTime]=DATETIME('NOW','LOCALTIME') WHERE [Users].[Id] = 1 */ //批量更新 //给所有女性朋友年轻 10 岁 context.Update<User>(a => new User() { Age = a.Age - 10, OpTime = DateTime.Now }, a => a.Gender == Gender.Woman); /* * UPDATE [Users] SET [Age]=([Users].[Age] - 10),[OpTime]=DATETIME('NOW','LOCALTIME') WHERE [Users].[Gender] = 2 */
方式2
以实体的方式更新:
User user = new User(); user.Id = 1; user.Name = "lu"; user.Age = 28; user.Gender = Gender.Man; user.OpTime = DateTime.Now; context.Update(user); //会更新所有映射的字段 /* * String @P_0 = 'lu'; Gender @P_1 = Man; Int32 @P_2 = 28; Nullable<Int32> @P_3 = NULL; DateTime @P_4 = '2016/8/6 22:05:02'; Int32 @P_5 = 1; UPDATE [Users] SET [Name]=@P_0,[Gender]=@P_1,[Age]=@P_2,[CityId]=@P_3,[OpTime]=@P_4 WHERE [Users].[Id] = @P_5 */ /* * 支持只更新属性值已变的属性 */ context.TrackEntity(user);//在上下文中跟踪实体 user.Name = user.Name + "1"; context.Update(user);//这时只会更新被修改的字段 /* * String @P_0 = 'lu1'; Int32 @P_1 = 1; UPDATE [Users] SET [Name]=@P_0 WHERE [Users].[Id] = @P_1 */
删除数据
方式1
以 lambda 表达式树的方式删除:
context.Delete<User>(a => a.Id == 1); /* * DELETE FROM [Users] WHERE [Users].[Id] = 1 */ //批量删除 //删除所有不男不女的用户 context.Delete<User>(a => a.Gender == null); /* * DELETE FROM [Users] WHERE [Users].[Gender] IS NULL */
方式2
以实体的方式删除:
User user = new User(); user.Id = 1; context.Delete(user); /* * Int32 @P_0 = 1; DELETE FROM [Users] WHERE [Users].[Id] = @P_0 */
支持函数

坎坎坷坷
SQLite 也是一个关系型数据库,之前开发 MySql Provider 的时候已经积累了些经验,因此,这回开发 SQLite 的 Provider 几乎不费吹灰之力。不过 SQLite 稍微比 MySql “恶心”那么一点:
当然,还有其它的不同,但都有变通的方式支持,就不一一列举了。
结语
从最初只支持 SqlServer 到现在支持3种数据库,瞬间感觉 Chloe “高大上”了许多。多数据库支持这路还很长,对于其他数据库的支持,只是时间问题。接下来的发展目标是...待定- -。在开源的一个多月里,要支持这支持那,每周得去学习,同时要不断的维护代码(咱得对得起关注 Chloe 的同学- -),真心有点儿小困,头发又少了很多555,接下来要好好休息放松一下先了。
文中代码都同步在 GitHub,地址:https://github.com/shuxinqin/Chloe/blob/master/src/DotNet/ChloeDemo/SQLiteDemo.cs。Demo 项目中已经包含 SQLite 驱动和数据文件,下载即可运行。
Chloe.ORM 完全开源,遵循 Apache2.0 协议,托管于 GitHub,地址:https://github.com/shuxinqin/Chloe。
相关介绍:http://www.cnblogs.com/so9527/p/5636216.html
性能测试:http://www.cnblogs.com/so9527/p/5674498.html