FreeSql 发展到现在, 已经有两种稳定的开发模式, 以下先简单带过一下. 后面才是本文的主题.
方法一: 基于 helper 的方式, 祼用;
dotnet add package FreeSql
提供 CodeFirst,DbFirst, 丰富的表达式树, 读写分离, AOP 等功能支持;
方法二: 基于 Repository + UnitOfWok 的方式;
dotnet add package FreeSql.Repository
这是一个扩展包, 提供标准的 IRepository 接口定义与默认实现, 以及 UnitOfWork 工作单元的支持, 更可怕的是集成了局部 / 全局过滤器, 实现租户, 软删除等功能不在话下.
不相信吗? 请看以下代码:
- public IServiceProvider ConfigureServices(IServiceCollection services) {
- services.AddSingleton<IFreeSql>(fsql);
- services.AddMvc();
- var builder = new ContainerBuilder();
- builder.RegisterFreeRepository(filter => filter
- .Apply<ISoftDelete>("SoftDelete", a => a.IsDeleted == false)
- .Apply<ITenant>("Tenant", a => a.TenantId == 1)
- );
- builder.Populate(services);
- var container = builder.Build();
- return new AutofacServiceProvider(container);
- }
比 abpvnext 还要方便, 因为 abp 的相关实体需要实现接口 ISoftDelete,ITenant;
我们没有这个限制, 只要过滤器的表达式解析成功, 就算可用;
使用在任何实体上的时候, 只要 [实体].IsDeleted == false 能解析能过, 就算可用;
方式三: 基于 DbContext
这个项目仍然是一个扩展包, 提类似 EFCore 那样的开发习惯. 目前定义的规则如下:
文字规则略显复杂, 后边有代码演示, 以及图文介绍在 SQLite 和 sqlserver 下的测试过程.
DbContext
提供 SaveChanges 方法;
执行队列;
DbSet
提供 Add,AddRange,Remove,RemoveRange,Update,UpdateRange 方法;
以及 Select 属性 (连去原有的 FreeSql 查询对象);
私有对象 states, 存储实体的副本哈希集合, key = 实体的主键值, value = 实体;
Add/AddRange(entitys)
验证 entitys 主键值, 是否存在于 states 中, 存在时报错;
验证 entitys 主键中存在自增:
若有, 则立即开启 DbContext 事务, 按数据库种类执行相应的方法, 最终将返回的自增值, 赋给 entitys 的属性;
若无, 并且 entitys 无主键值, 则报错;
否则, 进入 [打包执行队列] ;
完成时更新 states;
Remove/RemoveRange(entitys)
验证 entitys 主键值, 若无则报错;
验证 states 中是否存在, 若无则提醒应该先查询, 再删除;
删除 states 对应的实体;
清除 entitys 内的自增属性值, Guid 类型的值, 那这个 entitys 将变为可 Add 状态;
进入 [打包执行队列] ;
Update/UpdateRange(entitys)
验证 entitys 主键值, 若无则报错;
验证 states 中是否存在, 若无则提醒应该先查询, 再删除;
进入 [打包执行队列] ;
Select
立即执行队列中的命令 (打包方式), 以免脏读到未提交的数据;
查询完成时, 更新 states 的值;
更新数据规则
对比 states 中存在的历史快照值, 返回即将修改的 fields;
演示代码
- using FreeSql;
- public class SongContext : DbContext {
- public DbSet<Song> Songs { get; set; }
- public DbSet<Tag> Tags { get; set; }
- protected override void OnConfiguring(DbContextOptionsBuilder builder) {
- builder.UseFreeSql(这里是 IFreeeSql 对象);
- }
- }
- public class Song {
- [Column(IsIdentity = true)]
- public int Id { get; set; }
- public DateTime? Create_time { get; set; }
- public bool? Is_deleted { get; set; }
- public string Title { get; set; }
- public string Url { get; set; }
- }
- public class Tag {
- [Column(IsIdentity = true)]
- public int Id { get; set; }
- public string Name { get; set; }
- }
- using (var ctx = new SongContext()) {
- ctx.Songs.Select.Where(a => a.Id> 10).ToList();
- // 查询结果, 存入 states
- var song = new Song { };
- // 可插入的 song
- ctx.Songs.Add(song);
- id = song.Id;
- // 因有自增类型, 立即开启事务执行 SQL, 返回自增值
- var adds = Enumerable.Range(0, 100)
- .Select(a => new Song { Create_time = DateTime.Now, Is_deleted = false, Title = "xxxx" + a, Url = "url222" })
- .ToList();
- // 创建一堆无主键值的数据
- ctx.Songs.AddRange(adds);
- // 立即执行, 将自增值赋给 adds 所有元素, 因为有自增类型, 如果其他类型, 指定传入主键值, 不会立即执行
- for (var a = 0; a < adds.Count; a++)
- adds[a].Title = "dkdkdkdk" + a;
- ctx.Songs.UpdateRange(adds);
- // 批量修改, 进入队列
- ctx.Songs.RemoveRange(adds.Skip(10).Take(20).ToList());
- // 批量删除, 进入队列, 完成时 10-20 元素的主键值会被清除
- //ctx.Songs.Update(adds.First());
- adds.Last().Url = "skldfjlksdjglkjjcccc";
- ctx.Songs.Update(adds.Last());
- // 单条修改 urls 的值, 进入队列
- //throw new Exception("回滚");
- //ctx.Songs.Select.First();
- // 这里做一个查询, 会立即打包 [执行队列] , 避免没有提交的数据, 影响查询结果
- ctx.SaveChanges();
- // 打包 [执行队列] , 提交事务
- }
在 SQLite 测试
打个岔: 为什么一条条的执行?
有自增属性需要获取值;
SQLite 没有批量插入获取多个自增的办法, 或者您有招来支一支 (万分感谢);
后面采用 sqlserver 测试, 就不是这个境况了, insert into values(),(),(), 然后利用 output 特性返回所有值;
比较蛋疼的是, 这个特性不是所有数据库都有
可以看见, 最终 SaveChanges 时将不会产生影响的命令, 一起打包执行, 即采用优化合并的方式进行执行.
例如:
- ctx.Songs.Update(adds[0]);
- ctx.Songs.Update(adds[1]);
这两个更新操作, 会合成一条 SQL 命令执行.
在 sqlserver 测试
其实大致与 SQLite 下相同, 唯一的区别在于 AddRange 的处理方式, 如图:
当插入单条时, 采用了第一行代码的 SQL 命令;
当批量插入时, 采用了后面看上去复杂的 SQL 命令;
所有传入的实体属性值在执行完成后, 都会更新;
特别说明
FreeSql.DbContext 目前仍处于研究开发阶段, 不适合商用;
总结
为什么写这篇文章, 时常看见有人说某某 ORM 不是真正的 ORM, 没有 OO 思想.
希望 FreeSql.DbContext 随着时间的积累, 稳定性和成熟度有所提升, 不久成为一个真正的 ORM.
有人会担心, 我们第三方做的不靠谱, 没有 EFCore 稳定的说话, 这个是当然.
但是我们也有自己的特点, 不是吗? 我们可以做到多种数据库使用习惯的一致性, 这点 EFCore 目前是没有办法解决的难题.
从细节出发, 我们的口号是: 做 .NETCore 最方便的 ORM!
GitHub: https://github.com/2881099/FreeSql 377 星
还请献上宝贵的一星, 谢谢观看!!
来源: https://www.cnblogs.com/kellynic/p/10580936.html