FreeSql 经过半年的开发和坚持维护,在 0.6.x 版本中完成了几大重要事件:
1、按小包拆分,每个数据库实现为单独 dll;
2、实现 .net framework 4.5 支持;
3、同时支持 MySql.Data、MySqlConnector 的实现;
4、自定义导航属性关系的配置;
5、配套工具 FreeSql.Tools 发布;
本文主要讲解第5项,大主角往往在最后才出现!!!
拆分小包
在此之前一直被吐槽 FreeSql 臃肿,没有小包开发理念。其实我是一点也不承认这种评价,虽然刚开发只有一个 FreeSql.dll,但是在开发和规划上简单了很多。
有一条开发原则讲道:过早优化是恶梦!
大概意思是无论做什么项目,不要想着一开始就过度系统的、规范的执行。从外界来看是正规了,但是进度和稳定性会大大折扣。可以不信我,但是请一定要相信前人的总结啊!!!
从之前的一个 dll 到拆分成小包,我们总共耗时两天,虽然都在一个项目内开发,但其实耦合性并不高。
车到山前必有路,时机到了自然会拆。这个时机也是奠定 FreeSql 走出了稳定关键的一步。这样就会有更多人愿意加入 FreeSql 阵营。
- 各数据库单独包、延时加载包;
- FreeSql.Extensions.LazyLoading
- FreeSql.Provider.MySql
- FreeSql.Provider.PostgreSQL
- FreeSql.Provider.SqlServer
- FreeSql.Provider.Sqlite
- FreeSql.Provider.Oracle
支持 .netframework 4.5
早期 FreeSql 主要是在 .net core 最方便的 ORM!NETStandard 是新的标准,然而前段时间微软又说 ..net5 将合并。。。变化真的太快。
在实现拆分小包后,其实 FreeSql 的模块更加清淅,并且依赖项非常之少,然后比较容易的做出了 4.5 framework 的适配。
目前支持的版本:
Package Name | Version |
---|---|
FreeSql.Provider.MySql | NETStandard2.0、net452 |
FreeSql.Provider.PostgreSQL | NETStandard2.0、net45 |
FreeSql.Provider.SqlServer | NETStandard2.0、net451 |
FreeSql.Provider.Sqlite | NETStandard2.0、net45 |
FreeSql.Provider.Oracle | NETStandard2.0、net45 |
FreeSql.Extensions.LazyLoading | NETStandard2.0、net45 |
MySqlConnector 的实现
mysql 是一个神奇的流行数据库,在 .net 阵营中使用量排名老二。mysql 的版本五花八门,从 5.6 开始有了不同的分支,分支的出现使得 ado.net 驱动不通用。
很多人不推荐使用 MySql.Data 官方驱动,但是 FreeSql 一直在使用官驱,并且支持了所有 5.6 类型,包括 enum/set 等。
然后就有一些人,特别是高手的那些来提出要求,适配一个 MySqlConnector 的实现,然后著名的 A大(茶叔)提了一道 PR ,创建了 FreeSql.Provider.MySqlConnector 项目,99.9999% 源码和原来 FreeSql.Provider.MySql 相同,经过 266 个单元测试后发现,只需要兼容 enum/set 类型,参数化 ? @ 的处理就跑通了。然后就有了现在新的驱动包:
Package Name | Version |
---|---|
FreeSql.Provider.MySqlConnector | NETStandard2.0、net45 |
然后 FreeSqlBuilder 使用反射决定使用哪个 mysql 驱动。代码如下:
public IFreeSql<TMark> Build<TMark>() {
if (string.IsNullOrEmpty(_masterConnectionString)) throw new Exception("参数 masterConnectionString 不可为空,请检查 UseConnectionString");
IFreeSql<TMark> ret = null;
Type type = null;
switch(_dataType) {
case DataType.MySql:
type = Type.GetType("FreeSql.MySql.MySqlProvider`1,FreeSql.Provider.MySql")?.MakeGenericType(typeof(TMark));
if (type == null) type = Type.GetType("FreeSql.MySql.MySqlProvider`1,FreeSql.Provider.MySqlConnector")?.MakeGenericType(typeof(TMark));
if (type == null) throw new Exception("缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载");
break;
case DataType.SqlServer: type = Type.GetType("FreeSql.SqlServer.SqlServerProvider`1,FreeSql.Provider.SqlServer")?.MakeGenericType(typeof(TMark));
if (type == null) throw new Exception("缺少 FreeSql 数据库实现包:FreeSql.Provider.SqlServer.dll,可前往 nuget 下载");
break;
case DataType.PostgreSQL: type = Type.GetType("FreeSql.PostgreSQL.PostgreSQLProvider`1,FreeSql.Provider.PostgreSQL")?.MakeGenericType(typeof(TMark));
if (type == null) throw new Exception("缺少 FreeSql 数据库实现包:FreeSql.Provider.PostgreSQL.dll,可前往 nuget 下载");
break;
case DataType.Oracle: type = Type.GetType("FreeSql.Oracle.OracleProvider`1,FreeSql.Provider.Oracle")?.MakeGenericType(typeof(TMark));
if (type == null) throw new Exception("缺少 FreeSql 数据库实现包:FreeSql.Provider.Oracle.dll,可前往 nuget 下载");
break;
case DataType.Sqlite: type = Type.GetType("FreeSql.Sqlite.SqliteProvider`1,FreeSql.Provider.Sqlite")?.MakeGenericType(typeof(TMark));
if (type == null) throw new Exception("缺少 FreeSql 数据库实现包:FreeSql.Provider.Sqlite.dll,可前往 nuget 下载");
break;
default: throw new Exception("未指定 UseConnectionString");
}
ret = Activator.CreateInstance(type, new object[] { _masterConnectionString, _slaveConnectionString }) as IFreeSql<TMark>;
if (ret != null) {
ret.CodeFirst.IsAutoSyncStructure = _isAutoSyncStructure;
ret.CodeFirst.IsSyncStructureToLower = _isSyncStructureToLower;
ret.CodeFirst.IsSyncStructureToUpper = _isSyncStructureToUpper;
ret.CodeFirst.IsConfigEntityFromDbFirst = _isConfigEntityFromDbFirst;
ret.CodeFirst.IsNoneCommandParameter = _isNoneCommandParameter;
ret.CodeFirst.IsLazyLoading = _isLazyLoading;
var ado = ret.Ado as Internal.CommonProvider.AdoProvider;
ado.AopCommandExecuting += _aopCommandExecuting;
ado.AopCommandExecuted += _aopCommandExecuted;
}
return ret;
}
自定义导航属性关系的配置
FreeSql 原先支持约定式导航关系配置,对于新项目开发无疑可约定,但是很多老项目命名不规范的就使用不了相关的功能。
有关约定配置可参考 github wiki 中心文档
QQ 开发群真是个好平台,在发起讨论后,各位大佬都纷纷提出建议,最后以一票否决了各大建议,哈哈。。
主要从语法和用户使用的感受上设计,还是那个理念:日式简约!不能加入太多特性和功能,增加用户的理解和使用成本。
最终效果如下:
//导航属性,OneToMany
[Navigate("Song_id")]
public virtual List<song_tag> Obj_song_tag { get; set; }
//导航属性,ManyToOne/OneToOne
[Navigate("Song_id")]
public virtual song Obj_song { get; set; }
[Navigate("Tag_id")]
public virtual tag Obj_tag { get; set; }
然后就能使用很多导航的骚操作功能了。
配套工具 FreeSql.Tools 发布(主角压轴)
在此感谢这个工具的作者:mypeng1985,和参考者:movingsam
感谢有你们一帮热心的使用者,帮助 FreeSql 生态添砖加瓦!!
FreeSql 在早期做过一套生成器模板,功能比较隐秘,一般人不知道如何使用。。之后就一直沉迷于 CodeFirst 的功能开发,无法自拔。
然后在10天前,突然感觉 FreeSql 多了好多使用者,这个时间当然需要有从数据库生成实体的需求了!!
Q:没必要搞这种东西了吧 市面上蛮多的,或者搞一套模板完全搞定了?
A:
无法100%类型兼容啊,因为 FreeSql 支持的类型真的深,然后市场上的类型映射做不到 100% 匹配;
为了挖掘更多功能,生成器还需要有导航属性的支持,这是基本的,因为有导航属性后,FreeSql 操作会骚许多;
本来我发起了一个纯 winform 的生成器项目,界面都做好了如下:
是不是觉得很好看?我觉得好看。。。。其他人觉得丑。我在开发群发给大家看了之后,第二天 FreeSql.Tools 项目就搞出了新的界面,如下:
直接被秒杀了,这是来自作者:mypeng1985 的佳作。
界面看上去非常像 web,但其实不是的,仍然是一个 winform 程序,使用了 html 做界面,c# 做操作功能。
结束语
源码地址:https://github.com/2881099/FreeSql.Tools
FreeSql 从 2018年11月28日立项,开发,到今天 0.6.x,单元测试 1600+,生态也逐渐完善,有得到许多网友的鼓励和支持,感谢你们!感谢参与项目的你们!