前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >FreeSql.DbContext ,向"不是真正的 ORM" 说拜拜

FreeSql.DbContext ,向"不是真正的 ORM" 说拜拜

作者头像
梁规晓
发布2019-04-11 11:01:42
9600
发布2019-04-11 11:01:42
举报

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星

还请献上宝贵的一星,谢谢观看!!

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 DotNet程序园 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 方法一:基于 helper 的方式,祼用;
  • 方法二:基于 Repository + UnitOfWok 的方式;
  • 方式三:基于 DbContext
    • DbContext
      • DbSet
        • Add/AddRange(entitys)
          • Remove/RemoveRange(entitys)
            • Update/UpdateRange(entitys)
              • Select
                • 更新数据规则
                • 演示代码
                • 在 sqlite 测试
                • 在 sqlserver 测试
                  • 特别说明
                  • 总结
                  相关产品与服务
                  数据库
                  云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档