前言
FreeSql 目前版本号 0.5.5,预计明年元旦发布 1.0.0,切莫小看了版本号,目前单元测试方法1350+,并且每个方法内的涵盖面又比较广(不信的话见下图),每一次版本发布都作了较多的测试工作。
最近的一段时间,关注我们的人时不时会看见扩展包发布,今天振奋人心的功能主要是核心部分实现(扩展包今天当配角)。就不多啰嗦了,咱们直接入主题。
功能一:MapType 类型映射
使用 codefirst 时序列化 json 或 jsonb 数据类型报错
使用 postgresql + ef 配置数据映射关系,使用 FreeSql 的映射扩展时,对于json类型的数据映射出错。
这是来自 github 上的某个 issue,原因是用户的实体定义了 string,数据库类型为 json。FreeSql 本身支持了丰富的类型,不限于 json,但是限定了类型的映射,比如 JToken/JObject/JArray 的实体类型才可以映射至 PostgreSQL 数据库的 json 类型。另外虽然有 DbType 特性可以设置,但使用范围有限,不可跨越类型(如使用 string 可使用 DbType="char(100)")。
还有一些类似要求,如:将 enum 映射到数据库 varchar 的请求。。。。
到现在,我们已经彻底突破了这个障碍,基本可以做到随意映射类型。作为新项目开发,我们提供本身的默认类型映射已经非常人性化,提这些需求的人主要还是历史原因,咱们做程序维护工作的人员还是占比很高,千怪万怪只能怪 FreeSql 来得太迟。。。。
目前为 MapType 功能增加了大约 400 个单元测试方法。贴一段 demo 配置方法:
class EnumTestMap {
public Guid id { get; set; }
[Column(MapType = typeof(string))]
public ToStringMapEnum enum_to_string { get; set; }
[Column(MapType = typeof(string))]
public ToStringMapEnum? enumnullable_to_string { get; set; }
[Column(MapType = typeof(int))]
public ToStringMapEnum enum_to_int { get; set; }
[Column(MapType = typeof(int?))]
public ToStringMapEnum? enumnullable_to_int { get; set; }
[Column(MapType = typeof(string))]
public BigInteger biginteger_to_string { get; set; }
[Column(MapType = typeof(string))]
public BigInteger? bigintegernullable_to_string { get; set; }
}
public enum ToStringMapEnum { 中国人, abc, 香港 }
应该不需要解释了吧?
细看一下,实体内有 BigInteger 类型,这可是数据库无法表示的类型,现在就是可以使用(没辙)。但请注意:BigInteger 仅仅是 CURD 方便, Equals == 判断可以使用,无法使用 + - * / < > 等操作;
默认映射
csharp
MySql
SqlServer
PostgreSQL
Oracle
Sqlite
bool , bool?
bit(1)
bit
bool
number(1)
boolean
sbyte , sbyte?
tinyint(3)
smallint
int2
number(4)
smallint
short , short?
smallint(6)
smallint
int2
number(6)
smallint
int , int?
int(11)
int
int4
number(11)
integer
long , long?
bigint(20)
bigint
int8
number(21)
integer
byte , byte?
tinyint(3) unsigned
tinyint
int2
number(3)
int2
ushort , ushort?
smallint(5) unsigned
int
int4
number(5)
unsigned
uint , uint?
int(10) unsigned
bigint
int8
number(10)
decimal(10,0)
ulong , ulong?
bigint(20) unsigned
decimal(20,0)
numeric(20,0)
number(20)
decimal(21,0)
double , double?
double
float
float8
float(126)
double
float , float?
float
real
float4
float(63)
float
decimal , decimal?
decimal(10,2)
decimal(10,2)
numeric(10,2)
number(10,2)
decimal(10,2)
Guid , Guid?
char(36)
uniqueidentifier
uuid
char(36 CHAR)
character(36)
TimeSpan , TimeSpan?
time
time
time
interval day(2) to second(6)
bigint
DateTime , DateTime?
datetime
datetime
timestamp
timestamp(6)
datetime
DateTimeOffset , DateTimeOffset?
-
-
datetimeoffset
timestamp(6) with local time zone
-
Enum , Enum?
enum
int
int4
number(16)
mediumint
FlagsEnum , FlagsEnum?
set
bigint
int8
number(32)
bigint
byte[]
varbinary(255)
varbinary(255)
bytea
blob
blob
string
varchar(255)
nvarchar(255)
varchar(255)
nvarchar2(255)
nvarchar(255)
MygisPoint
point
-
-
-
-
MygisLineString
linestring
-
-
-
-
MygisPolygon
polygon
-
-
-
-
MygisMultiPoint
multipoint
-
-
-
-
MygisMultiLineString
multilinestring
-
-
-
-
MygisMultiPolygon
multipolygon
-
-
-
-
BitArray
-
-
varbit(64)
-
-
NpgsqlPoint , NpgsqlPoint?
-
-
point
-
-
NpgsqlLine , NpgsqlLine?
-
-
line
-
-
NpgsqlLSeg , NpgsqlLSeg?
-
-
lseg
-
-
NpgsqlBox , NpgsqlBox?
-
-
box
-
-
NpgsqlPath , NpgsqlPath?
-
-
path
-
-
NpgsqlPolygon , NpgsqlPolygon?
-
-
polygon
-
-
NpgsqlCircle , NpgsqlCircle?
-
-
circle
-
-
(IPAddress Address, int Subnet) , (IPAddress Address, int Subnet)?
-
-
cidr
-
-
IPAddress
-
-
inet
-
-
PhysicalAddress
-
-
macaddr
-
-
NpgsqlRange , NpgsqlRange?
-
-
int4range
-
-
NpgsqlRange , NpgsqlRange?
-
-
int8range
-
-
NpgsqlRange , NpgsqlRange?
-
-
numrange
-
-
NpgsqlRange , NpgsqlRange?
-
-
tsrange
-
-
PostgisPoint
-
-
geometry
-
-
PostgisLineString
-
-
geometry
-
-
PostgisPolygon
-
-
geometry
-
-
PostgisMultiPoint
-
-
geometry
-
-
PostgisMultiLineString
-
-
geometry
-
-
PostgisMultiPolygon
-
-
geometry
-
-
PostgisGeometry
-
-
geometry
-
-
PostgisGeometryCollection
-
-
geometry
-
-
Dictionary
-
-
hstore
-
-
JToken
-
-
jsonb
-
-
JObject
-
-
jsonb
-
-
JArray
-
-
jsonb
-
-
数组
-
-
以上所有类型都支持
-
-
以上类型和长度是默认值,可手工设置,如 string 属性可指定 [Column(DbType = "varchar(max)")]
功能二:唯一键(Unique)
class AddUniquesInfo {
public Guid id { get; set; }
[Column(Unique = "uk_phone")]
public string phone { get; set; }
[Column(Unique = "uk_group_index")]
public string group { get; set; }
[Column(Unique = "uk_group_index")]
public int index { get; set; }
[Column(Unique = "uk_group_index222")]
public string index22 { get; set; }
}
Unique 指定相同的标识,代表联合唯一键,现已支持迁移。
功能三:弱类型(实体)
之前在操作实体时,必须传统泛型参数,现在可以实现弱类型实体的操作。以 Repository 为例:
var repos = fsql.GetGuidRepository();
repos.AsType(typeof(AddUpdateInfo));
var item = new AddUpdateInfo();
repos.Insert(item);
item.Clicks += 1;
repos.InsertOrUpdate(item);
var item2 = repos.Find(item.Id) as AddUpdateInfo;
Assert.Equal(item.Clicks, item2.Clicks);
repos.DataFilter.Apply("xxx", a => (a as AddUpdateInfo).Clicks == 11);
Assert.Null(repos.Find(item.Id));
然后呢,DbContext 也支持同样的操作。
dotnet add package FreeSql.DbContext
功能四:ToList & Mapper
现在支持 ToList(a => new Dto()) 这样的简单数据映射。
什么意思?即 Dto 只要有属性名与实体属性相同,就会根据匹配到的字段查询(不是查询所有字段回来再映射)。
然后这个骚操作,还支持多表查询的映射,怎么解决多表存在相同名字的字段问题呢?优先级规则,它会依次序匹配 LeftJoin/InnerJoin/RightJoin 的实体。
功能五:ToList(true) 贪婪加载
以前 .ToList() 会加载两级Join对象;
现在 ISelect.ToList(includeNestedMembers: true) 贪婪加载所有 LeftJoin/InnerJoin/RightJoin 导航数据,不论对象的层级;
功能六:WhereDynamic 动态条件
支持传入动态对象如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1}。
也就是说 WhereDynaimc 方法输入类型为 object,是不是很方便?还支持联合主键呢。
功能七:IAdo.Query 多个结果集
var result = fsql.Ado.Query("select from t1; select from t2");
功能八:开发环境之利器:MVC 中间件 FreeSql.AdminLTE
大约是前一段时间的某一天(废话),因为使用 FreeSql 的某项目需要做一个简单的后台功能,以便录入或管理数据。在实施的过程中好怀念当初 dotnetGen 生成器的味道,用它产生 curd 基本功能几乎是秒做;
FreeSql.AdminLTE,是的就是它,前段时间发布过一次。
它是 FreeSql 衍生出来的 .NETCore MVC 中间件、中间件、中间件(重复三遍)扩展包,基于 AdminLTE 前端框架动态产生实体的增删查改界面;
输入:实体1、实体2、实体3
输出:后台管理的功能
只需要传入实体,就可以形成 curd 的管理功能,是不是有些骚啊~~~
先发一张运行后的图片尝个鲜:
这是根据实体产生 curd 界面的 mvc 中间件,开发时预览数据好方便啊。看完预览图不由得再感叹一次 FreeSql 的易用性,那句口号:做 .NETCore 最方便的 ORM! 没有说错。。。作者多次提及:“我们是日式简约风格,没那么复杂的用法”,也验证了这一点。。
添加/修改
中件间产生的界面包括添加、修改数据的功能,普通实体的根据属性的类型与 Html5 UI 一一映射;
比较特殊的映射规则:
c# 类型
Html5
布尔
复选框
枚举
下拉选择
日期
日期控件
ManyToOne 导航属性
下拉选择
ManyToMany 导航属性
多选器
等等。。。
什么情况会产生【上传文件】控件?
有兴趣的可以了解源码,目前没有开放在外部配置。
查询/过滤
中件间为每个实体提供了分页列表查询,每页为20条数据;
除此外,还提供了过滤条件的支持,规则是根据导航属性(ManyToOne、ManyToMany)。比如【文章实体】,内含有【分类id】+【分类对象】,则【文章】列表页会出现按【分类】筛选的UI,详见上面的 demo 示意图,或者下载对应的 demo 版本运行;
删除
中件间为每个实体提供了批量删除的功能;
测试 demo
我们习惯用 sqlite 做测试库,测试完毕直接删除目录,不留垃圾数据,所以下面的 demo 不需要修改任何地方,运行时自动建库、建表;
提供 .net core 2.1、2.2 两种环境的测试 demo 下载:
第一步:
dotnet restore
第二步:
dotnet run
思考
一番惊喜过后,你应该会考虑实用性,这样做有什么价值,可用于什么样的场景?
这个扩展包简单的输入,产生巨量的功能反馈。目前来说它是死板的,对外提供的扩展性几乎为零,这样也就限定了它的应用场景。
不合适的场景
1、它不可替代我们自身开发的后台管理系统;
2、它不适合摆放在公网正式环境,存在数据安全问题;
3、欢迎补充。。。;
谈谈定位
目前的定位是这样的,在开发环境中使用,查阅预览实体数据,同时也比较方便的管理测试数据。
一段拥有无比力量的小段代码,也是中间件界面的功能开启:
//可以配置子目录访问,如:/testadmin/
app.UseFreeAdminLTE("/",
typeof(Entities.Song),
typeof(Entities.Tag));
观后抽奖
我们一直在发布纯技术干货的分享文章,FreeSql 已经基本完成 .NETCore 最方便的 ORM 使命,我们正在筹备生态的建立,比如 ABP 中如何使用 FreeSql 的实现,需要各种各样的扩展包,好多好多工作量。有没有大神愿意无偿参与做这件事情,好吧。。应该没有人!!如果你回心转意了,欢迎联系我们。
欢迎持续关注我们,做 .NETCore 最方便的 ORM !
(QQ群:4336577)
说好的抽奖呢???