From d33109c3b10b29313070e944196894467e71666a Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Wed, 14 Aug 2024 13:09:14 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20KingBaseES=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E6=95=B0=E7=BB=84=E7=AD=89=E7=B1=BB=E5=9E=8B=EF=BC=88?= =?UTF-8?q?=E5=8F=82=E8=80=83=20PostgreSQL=EF=BC=89=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FreeSql/FreeSql.xml | 1685 ++++++++--------- .../FreeSql.Provider.KingbaseES.csproj | 6 +- .../KingbaseESAdo/KingbaseESAdo.cs | 41 +- .../KingbaseESCodeFirst.cs | 56 +- .../KingbaseESDbFirst.cs | 178 +- .../KingbaseESExpression.cs | 90 +- .../KingbaseESExtensions.cs | 8 + .../KingbaseESProvider.cs | 81 +- .../KingbaseESUtils.cs | 64 +- 9 files changed, 1280 insertions(+), 929 deletions(-) diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 44d4f9e4a..c021f7a98 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1087,93 +1087,6 @@ - - - 动态创建实体类型 - - - - - 配置Class - - 类名 - 类标记的特性[Table(Name = "xxx")] [Index(xxxx)] - - - - - 获取类型构建器,可作为要构建的Type来引用 - - - - - 配置属性 - - 属性名称 - 属性类型 - 属性标记的特性-支持多个 - - - - - 配置属性 - - 属性名称 - 属性类型 - 该属性是否重写父类属性 - 属性标记的特性-支持多个 - - - - - 配置属性 - - 属性名称 - 属性类型 - 该属性是否重写父类属性 - 属性默认值 - 属性标记的特性-支持多个 - - - - - 配置父类 - - 父类类型 - - - - - Override属性 - - - - - - Emit动态创建出Class - Type - - - - - - Emit动态创建出Class - Type,不附带获取TableInfo - - - - - - 首字母小写 - - - - - - - 首字母大写 - - - - 获取实体的主键值,以 "*|_,[,_|*" 分割,当任意一个主键属性无值时,返回 "" @@ -2883,6 +2796,85 @@ lambda表达式 + + + 按列排序,OrderBy(a => a.Time) + + + + + + + + 实现 select .. from ( select ... from t ) a 这样的功能 + 使用 AsTable 方法也可以达到效果 + 示例:WithSql("select * from id=@id", new { id = 1 }) + 提示:parms 参数还可以传 Dictionary<string, object> + + SQL语句 + 参数 + + + + + 实现 select .. from ( select .. UNION ALL select .. ) a 这样的功能(基于内存数据) + + 内存数据 + + + + + 嵌套查询 select * from ( select ... from table ... ) a + + + + + + + + 查询条件,Where(a => a.Id > 10),支持导航对象查询,Where(a => a.Author.Email == "2881099@qq.com") + + lambda表达式 + + + + + 查询条件,Where(true, a => a.Id > 10),支导航对象查询,Where(true, a => a.Author.Email == "2881099@qq.com") + + true 时生效 + lambda表达式 + + + + + 按列排序,OrderBy(a => a.Time) + + + + + + + + 按列倒向排序,OrderByDescending(a => a.Time) + + 列 + + + + + 按聚合条件过滤,Having(a => a.Count() > 10) + + lambda表达式 + + + + + 按聚合条件过滤,HavingIf(true, a => a.Count() > 10) + + true 时生效 + lambda表达式 + + 按列排序,OrderBy(a => a.Time) @@ -3992,406 +3984,381 @@ - 标识符,可将 CurdBefore 与 CurdAfter 进行匹配 - - - - - 操作类型 - + 标识符,可将 CurdBefore 与 CurdAfter 进 表名,如:dbo.table1 + 是否忽略大小写 + - + - 实体类型 + 获取数据库枚举类型int值 + + - + - 实体类型的元数据 + 获取c#转换,(int)、(long) + + - + - 执行的 SQL + 获取c#值 + + - + - 参数化命令 + 获取c#类型,int、long + + - + - 状态数据,可与 CurdAfter 共享 + 获取c#类型对象 + + - + - 发生的错误 + 获取ado.net读取方法, GetBoolean、GetInt64 + + - + - 执行SQL命令,返回的结果 + 序列化 + + - + - 耗时(单位:Ticks) + 反序列化 + + - + - 耗时(单位:毫秒) + 获取数据库枚举类型,适用 PostgreSQL + + - + - 标识符,可将 SyncStructureBeforeEventArgs 与 SyncStructureAfterEventArgs 进行匹配 + 临时 LambdaExpression.Parameter - + - 实体类型 + 如果实体类有自增属性,分成两个 List,有值的Item1 merge,无值的Item2 insert + + - + - 状态数据,可与 SyncStructureAfter 共享 + AsType, Ctor, ClearData 三处地方需要重新加载 - + - 执行的 SQL + AsType, Ctor, ClearData 三处地方需要重新加载 - + - 发生的错误 + 动态读取 DescriptionAttribute 注释文本 + + - + - 耗时(单位:Ticks) + 通过属性的注释文本,通过 xml 读取 + + Dict:key=属性名,value=注释 - + - 耗时(单位:毫秒) + 更新实体的元数据 - + - 类型 + 执行更新的 SQL - + - 属性列的元数据 + 执行更新命令的参数 - + - 反射的属性信息 + 执行更新命令影响的行 - + - 获取实体的属性值,也可以设置实体的属性新值 + 更新的实体数量 - + - 实体对象 + 更新的实体 - + - 中断实体对象审计 - false: 每个实体对象的属性都会审计(默认) - true: 每个实体对象只审计一次 + 映射优先级,默认: Attribute > FluentApi > Aop - + - ADO.NET 数据流读取对象 + 实体特性 + [Table(Name = "tabname")] + [Column(Name = "table_id")] - + - DataReader 对应的 Index 位置 + 流式接口 + fsql.CodeFirst.ConfigEntity(a => a.Name("tabname")) + fsql.CodeFirst.ConfigEntity(a => a.Property(b => b.Id).Name("table_id")) - + - DataReader 对应的 PropertyInfo + AOP 特性 https://github.com/dotnetcore/FreeSql/wiki/AOP + fsql.Aop.ConfigEntity += (_, e) => e.ModifyResult.Name = "public.tabname"; + fsql.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = "table_id"; - + - 获取 Index 对应的值,也可以设置拦截的新值 + 不进行任何处理 - + - 标识符,可将 CommandBefore 与 CommandAfter 进行匹配 + 将帕斯卡命名字符串转换为下划线分隔字符串 + + BigApple -> Big_Apple - + - 状态数据,可与 CommandAfter 共享 + 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全大写 + + BigApple -> BIG_APPLE - + - 发生的错误 + 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全小写 + + BigApple -> big_apple - + - 执行SQL命令,返回的结果 + 将字符串转换为大写 + + BigApple -> BIGAPPLE - + - 耗时(单位:Ticks) + 将字符串转换为小写 + + BigApple -> bigapple - + - 耗时(单位:毫秒) + 创建一个过滤器 + 提示:在 Lambda 中判断登陆身份,请参考资料 AsyncLocal + + 名字 + 表达式 + 条件在最前面 + - + - 标识符,可将 TraceBeforeEventArgs 与 TraceAfterEventArgs 进行匹配 + 创建一个动态过滤器,当 condition 返回值为 true 时才生效 + 场景:当登陆身份是管理员,则过滤条件不生效 + 提示:在 Lambda 中判断登陆身份,请参考资料 AsyncLocal + + 名字 + 委托,返回值为 true 时才生效 + 表达式 + 条件在最前面 + - + - 状态数据,可与 TraceAfter 共享 - - - - - 备注 - - - - - 发生的错误 - - - - - 耗时(单位:Ticks) - - - - - 耗时(单位:毫秒) - - - - - 【开发环境必备】自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改 - - - - - 转小写同步结构,适用 PostgreSQL - - - - - 转大写同步结构,适用 Oracle/达梦/人大金仓 - - - - - 将数据库的主键、自增、索引设置导入,适用 DbFirst 模式,无须在实体类型上设置 [Column(IsPrimary)] 或者 ConfigEntity。此功能目前可用于 mysql/sqlserver/postgresql/oracle。 - 本功能会影响 IFreeSql 首次访问的速度。 - 若使用 CodeFirst 创建索引后,又直接在数据库上建了索引,若无本功能下一次 CodeFirst 迁移时数据库上创建的索引将被删除 - - - - - 不使用命令参数化执行,针对 Insert/Update - - - - - 是否生成命令参数化执行,针对 lambda 表达式解析 - 注意:常量不会参数化,变量才会做参数化 - var id = 100; - fsql.Select<T>().Where(a => a.id == id) 会参数化 - fsql.Select<T>().Where(a => a.id == 100) 不会参数化 + 创建一个过滤器(实体类型 属于指定 TEntity 才会生效) + 提示:在 Lambda 中判断登陆身份,请参考资料 AsyncLocal + + 名字 + 表达式 + 条件在最前面 + - + - 延时加载导航属性对象,导航属性需要声明 virtual + 创建一个过滤器(实体类型 属于指定 TEntity 才会生效) + 场景:当登陆身份是管理员,则过滤条件不生效 + 提示:在 Lambda 中判断登陆身份,请参考资料 AsyncLocal + + 名字 + 委托,返回值为 true 时才生效 + 表达式 + 条件在最前面 + - + - 将实体类型与数据库对比,返回DDL语句 + 使用指定 DbConnection 连接执行 - + - + - 将实体类型集合与数据库对比,返回DDL语句 + 使用指定 DbTransaction 事务执行 - 实体类型 + - + - 将实体类型与数据库对比,返回DDL语句(指定表名) + 增加参数化对象 - 实体类型 - 指定表名对比 + 参数名 + 参数值 + 修改本次创建好的参数化对象,比如将 parameterName 参数修改为 Output 类型 - + - 同步实体类型到数据库 - 注意:生产环境中谨慎使用 + 设置执行的命令类型,SQL文本、或存储过程 - + + - + - 同步实体类型集合到数据库 - 注意:生产环境中谨慎使用 + 设置命令执行超时(秒) - + + - + - 同步实体类型到数据库(指定表名) - 注意:生产环境中谨慎使用 + 分页信息 - 实体类型 - 指定表名对比 - 强制同步结构,无视缓存每次都同步 - + - 根据 System.Type 获取数据库信息 + 第几页,从1开始 - - - + - FreeSql FluentApi 配置实体,方法名与特性相同 + 每页多少 - - - - + - FreeSql FluentApi 配置实体,方法名与特性相同 + 查询的记录数量 - - - - + - 获取 FreeSql FluentApi 配置实体的元数据 + 当前操作的数据 - - 未使用ConfigEntity配置时,返回null - + - 获取实体类核心配置 + 当前批次 - - - + - 获取所有数据库 + 总批次数量 - - + - 获取指定数据库的表信息,包括表、列详情、主键、唯一键、索引、外键、备注 + 获取 obj.CsName 属性值 MapType 之后的数据库值 - + - + - 获取指定单表信息,包括列详情、主键、唯一键、索引、备注 + 获取 obj.CsName 属性原始值(不经过 MapType) - 表名,如:dbo.table1 - 是否忽略大小写 - + - + - 判断表是否存在 + 设置 obj.CsName 属性值 - 表名,如:dbo.table1 - 是否忽略大小写 - + + - + - 获取数据库枚举类型int值 + 动态过滤条件 - - - + - 获取c#转换,(int)、(long) + 属性名:Name + 导航属性:Parent.Name + 多表:b.Name - - - + - 获取c#值 + 操作符 - - - + - 获取c#类型,int、long + 值 - - - + - 获取c#类型对象 + Filters 下的逻辑运算符 - - - + - 获取ado.net读取方法, GetBoolean、GetInt64 + 子过滤条件,它与当前的逻辑关系是 And + 注意:当前 Field 可以留空 - - - + - 序列化 - - - + like @@ -4489,288 +4456,299 @@ 流式接口 fsql.CodeFirst.ConfigEntity(a => a.Name("tabname")) - fsql.CodeFirst.ConfigEntity(a => a.Property(b => b.Id).Name("table_id")) + fsql.CodeFirst.ConfigEn, string value) => value; + } + } - + - AOP 特性 https://github.com/dotnetcore/FreeSql/wiki/AOP - fsql.Aop.ConfigEntity += (_, e) => e.ModifyResult.Name = "public.tabname"; - fsql.Aop.ConfigEntityProperty += (_, e) => e.ModifyResult.Name = "table_id"; + 授权 DynamicFilter 支持 Custom 自定义解析 - + - 不进行任何处理 + 是否放弃继续读取 - + - 将帕斯卡命名字符串转换为下划线分隔字符串 - - BigApple -> Big_Apple + 中间表,多对多 - + - 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全大写 - - BigApple -> BIG_APPLE + PostgreSQL 数组类型专属功能 + 方式一:select * from Role where Id in (RoleIds) + class User { + ____public int[] RoleIds { get; set; } + ____[Navigate(nameof(RoleIds))] + ____public List<Role> Roles { get; set; } + } + 方式二:select * from User where RoleIds @> Id + class Role { + ____public int Id { get; set; } + ____[Navigate(nameof(User.RoleIds))] + ____public List<User> Users { get; set; } + } - + - 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全小写 - - BigApple -> big_apple + 是否可用 - + - 将字符串转换为大写 - - BigApple -> BIGAPPLE + 不可用错误 - + - 将字符串转换为小写 - - BigApple -> bigapple + 不可用时间 - + - 创建一个过滤器 - 提示:在 Lambda 中判断登陆身份,请参考资料 AsyncLocal + 将对象池设置为不可用,后续 Get/GetAsync 均会报错,同时启动后台定时检查服务恢复可用 - - 名字 - 表达式 - 条件在最前面 - + + + 由【可用】变成【不可用】时返回true,否则返回false - + - 创建一个动态过滤器,当 condition 返回值为 true 时才生效 - 场景:当登陆身份是管理员,则过滤条件不生效 - 提示:在 Lambda 中判断登陆身份,请参考资料 AsyncLocal + 统计对象池中的对象 - - 名字 - 委托,返回值为 true 时才生效 - 表达式 - 条件在最前面 - - + - 创建一个过滤器(实体类型 属于指定 TEntity 才会生效) - 提示:在 Lambda 中判断登陆身份,请参考资料 AsyncLocal + 统计对象池中的对象(完整) - - 名字 - 表达式 - 条件在最前面 - - + - 创建一个过滤器(实体类型 属于指定 TEntity 才会生效) - 场景:当登陆身份是管理员,则过滤条件不生效 - 提示:在 Lambda 中判断登陆身份,请参考资料 AsyncLocal + 获取资源 - - 名字 - 委托,返回值为 true 时才生效 - 表达式 - 条件在最前面 + 超时 - + - 使用指定 DbConnection 连接执行 + 使用完毕后,归还资源 - - + 对象 + 是否重新创建 - + - 使用指定 DbTransaction 事务执行 + 名称 - - - + - 增加参数化对象 + 池容量 - 参数名 - 参数值 - 修改本次创建好的参数化对象,比如将 parameterName 参数修改为 Output 类型 - - + - 设置执行的命令类型,SQL文本、或存储过程 + 默认获取超时设置 - - - + - 设置命令执行超时(秒) + 空闲时间,获取时若超出,则重新创建 - - - + - 分页信息 + 异步获取排队队列大小,小于等于0不生效 - + - 第几页,从1开始 + 获取超时后,是否抛出异常 - + - 每页多少 + 监听 AppDomain.CurrentDomain.ProcessExit/Console.CancelKeyPress 事件自动释放 - + - 查询的记录数量 + 后台定时检查可用性间隔秒数 - + - 当前操作的数据 + 权重 - + - 当前批次 + 对象池的对象被创建时 + 返回被创建的对象 - + - 总批次数量 + 销毁对象 + 资源对象 - + - 获取 obj.CsName 属性值 MapType 之后的数据库值 + 从对象池获取对象超时的时候触发,通过该方法统计 - + + + + 从对象池获取对象成功的时候触发,通过该方法统计或初始化对象 + + 资源对象 + + + + 归还对象给对象池的时候触发 + + 资源对象 + + + + 检查可用性 + + 资源对象 - + - 获取 obj.CsName 属性原始值(不经过 MapType) + 事件:可用时触发 - - + - 设置 obj.CsName 属性值 + 事件:不可用时触发 - - - + - 动态过滤条件 + 所属对象池 - + - 属性名:Name - 导航属性:Parent.Name - 多表:b.Name + 在对象池中的唯一标识 - + - 操作符 + 资源对象 - + - 值 + 被获取的总次数 - + + 最后获取时的时间 + + - Filters 下的逻辑运算符 + 最后归还时的时间 - + - 子过滤条件,它与当前的逻辑关系是 And - 注意:当前 Field 可以留空 + 创建时间 - + + + 最后获取时的线程id + + + + + 最后归还时的线程id + + + + + 重置 Value 值 + + + - like + 对象池管理类 + 对象类型 - + - = - Equal/Equals/Eq 效果相同 + 后台定时检查可用性 + - + - = - Equal/Equals/Eq 效果相同 + 创建对象池 + 池大小 + 池内对象的创建委托 + 获取池内对象成功后,进行使用前操作 - + - = - Equal/Equals/Eq 效果相同 + 创建对象池 + 策略 - + - <> + 获取可用资源,或创建资源 + - + - > + 用于解决多实例情况下的静态集合缓存问题 - + - >= + + String resources used in FreeSql exceptions, etc. + + + These strings are exposed publicly for use by database providers and extensions. + It is unusual for application code to need these strings. + - + - < + 重写当前线程的 CurrentUICulture 属性,对 + 使用此强类型资源类的所有资源查找执行重写。 - + - <= + [Table(AsTable = "{asTable}")] 特性值格式错误 - + - >= and < - 此时 Value 的值格式为逗号分割:value1,value2 或者数组 + [Table(AsTable = xx)] 设置的属性名 {atmGroupsValue} 不是 DateTime 类型 - + - >= and < - 此时 Value 的值格式为逗号分割:date1,date2 或者数组 - 这是专门为日期范围查询定制的操作符,它会处理 date2 + 1,比如: + {name}: Failed to get resource {statistics} + + + - 导航属性 {trytbTypeName}.{pnvName} 没有找到对应的字段,如:{pnvName}{findtbrefPkCsName}、{pnvName}_{findtbrefPkCsName}。或者使用 [Navigate] 特性指定关系映射。 + 导航属性 {trytbTypeName}.{pnvName} 没有找到对应的字段,如:{pnvName}{findtbrefPkCsName}、{pnvName}_{findtbrefPkCsName}。或者使用 [Navigate] 特性指定 + + + 未实现该功能 - + - 导航属性 {trytbTypeName}.{pnvName} 解析错误,实体类型 {trytcTypeName} 缺少主键标识,[Column(IsPrimary = true)] + 未实现错误,请反馈给作者 - + - 导航属性 {trytbTypeName}.{pnvName} 解析错误,{trytbCsName}.{trycolCsName} 和 {tbrefCsName}.{tbrefPrimarysCsName} 类型不一致 + 找不到 {name} - + - 导航属性 {trytbTypeName}.{pnvName} 特性 [Navigate] 解析错误,在 {tbrefTypeName} 未找到属性:{bi} + FreeSql.Provider.{providerName} 未实现 Skip/Offset 功能,如果需要分页请使用判断上一次 id - + - {tableTypeDisplayCsharp} 没有定义主键,无法使用 SetSource,请尝试 SetDto 或者 SetSource 指定临时主键 + 对象池 - + - 没有定义属性 + 旧表(OldName):{tboldname} 存在,数据库已存在 {tbname} 表,无法改名 - + - 未实现 + OnConflictDoUpdate 功能要求实体类必须设置 IsPrimary 属性 - + - 未实现函数表达式 {exp} 解析 + Oracle CodeFirst 不支持代码创建 tablespace 与 schemas {tbname} - + - 未实现函数表达式 {exp} 解析,参数 {expArguments} 必须为常量 + 解析失败 {callExpMethodName} {message},提示:可以使用扩展方法 IQueryable.RestoreToSelect() 还原为 ISelect 再查询 - + - 未实现函数表达式 {exp} 解析,如果正在操作导航属性集合,请使用 .AsSelect().{exp3MethodName}({exp3ArgumentsCount}) + fsql.InsertOrUpdate + IfExistsDoNothing + {providerName}要求实体类 {tableCsName} 必须有主键 - + - 未实现 MemberAccess 下的 Constant + SelectMany 错误的类型:{typeFullName} - + - 未实现 {name} + 从库 - + - 不支持 + 类型 {objentityTypeFullName} 不可迁移 - + - {dataType} 不支持 OrderByRandom 随机排序 + 类型 {objentityTypeFullName} 不可迁移,可迁移属性0个 - + - {property} 不是有效的导航属性 + 未实现 {columnDbTypeTextFull} 类型映射 - + - {dbName} 找不到列 {memberName} + 错误的参数设置:{args} - + - 找不到 {CsName} 对应的列 + {qoteSql} is NULL,除非设置特性 [Column(IsNullable = false)] - + - 找不到属性:{memberName} + 分表字段值 "{dt}" 不能小于 "{beginTime} " - + - 找不到属性名 {proto} + 分表字段值不能为 null - + - Custom 找不到对应的{{ 反射信息 }}:{fiValueCustomArray} + 分表字段值 "{columnValue}" 不能转化成 DateTime - + - Custom 找不到对应的{{ 静态方法名 }}:{fiValueCustomArray} + 分表字段值 "{dt}" 未匹配到分表名 - + - [Table(AsTable = xx)] 设置的属性名 {atmGroupsValue} 不存在 + T2 类型错误 - + - 未指定 UseConnectionString 或者 UseConnectionFactory + tableName 格式错误,示例:“log_{yyyyMMdd}” - + - 【{policyName}】ObjectPool.{GetName}() timeout {totalSeconds} seconds, see: https://github.com/dotnetcore/FreeSql/discussions/1081 + {Type}.AsType 参数错误,请传入正确的实体类型 - + - 【{policyName}】ObjectPool.GetAsync() The queue is too long. Policy.AsyncGetCapacity = {asyncGetCapacity} + {thatFullName} 类型无法访问构造函数 - + - 【OneToMany】导航属性 {trytbTypeName}.{pnvName} 在 {tbrefCsName} 中没有找到对应的字段,如:{findtrytb}{findtrytbPkCsName}、{findtrytb}_{findtrytbPkCsName} + {name} 类型错误 - + - 【OneToMany】导航属性 {trytbTypeName}.{pnvName} 解析错误,{trytbCsName}.{trytbPrimarysCsName} 和 {tbrefCsName}.{trycolCsName} 类型不一致 + {Type}.AsType 参数不支持指定为 object - + - 、{refpropName}{findtrytbPkCsName}、{refpropName}_{findtrytbPkCsName}。或者使用 [Navigate] 特性指定关系映射。 + 类型 {typeofFullName} 错误,不能使用 IncludeMany - + - 参数 field 未指定 + 无法解析表达式:{exp} - + - {property} 参数错误,它不是集合属性,必须为 IList<T> 或者 ICollection<T> + 无法解析表达式方法 {exp3tmpCallMethodName} - + - {property} 参数错误,它不是有效的导航属性 + 请使用 fsql.InsertDict(dict) 方法插入字典数据 - + - {where} 参数错误,{keyval} 不是有效的属性名,在实体类 {reftbTypeDisplayCsharp} 无法找到 + C#: that >= between && that <= and + SQL: that BETWEEN between AND and + + + + - + - {property} 参数错误,格式 "TopicId=Id,多组使用逗号连接" + 注意:这个方法和 Between 有细微区别 + C#: that >= start && that < end + SQL: that >= start and that < end + + + + - + - 解析失败 {callExpMethodName} {message} + 获取 Type 的原始 c# 文本表示 + + + - + - 【{policyName}】The ObjectPool has been disposed, see: https://github.com/dotnetcore/FreeSql/discussions/1079 + 测量两个经纬度的距离,返回单位:米 + 经纬坐标1 + 经纬坐标2 + 返回距离(单位:米) - + - 【{policyName}】状态不可用,等待后台检查程序恢复方可使用。{UnavailableExceptionMessage} + 将 IEnumable<T> 转成 ISelect<T>,以便使用 FreeSql 的查询功能。此方法用于 Lambda 表达式中,快速进行集合导航的查询。 + + + - + - 属性{trytbVersionColumnCsName} 被标注为行锁(乐观锁)(IsVersion),但其必须为数字类型 或者 byte[] 或者 string,并且不可为 Nullable + 多表查询 + - + - properties 参数不能为空 + 本方法实现从已知的内存 List 数据,进行和 ISelect.IncludeMany 相同功能的贪婪加载 + 示例:new List<Song>(new[] { song1, song2, song3 }).IncludeMany(fsql, a => a.Tags); + 文档:https://github.com/dotnetcore/FreeSql/wiki/%E8%B4%AA%E5%A9%AA%E5%8A%A0%E8%BD%BD + + + + + 选择一个集合的导航属性,如: .IncludeMany(a => a.Tags) + 可以 .Where 设置临时的关系映射,如: .IncludeMany(a => a.Tags.Where(tag => tag.TypeId == a.Id)) + 可以 .Take(5) 每个子集合只取5条,如: .IncludeMany(a => a.Tags.Take(5)) + 可以 .Select 设置只查询部分字段,如: (a => new TNavigate { Title = a.Title }) + + 即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?) + - + - {property} 属性名无法找到 + 本方法实现从已知的内存 List 数据,进行和 ISelect.IncludeMany/Include 相同功能的贪婪加载 + 集合:new List<Song>(new[] { song1, song2, song3 }).IncludeByPropertyName(fsql, "Tags", "ParentId=Id", 5, "Id,Name"); + 普通:new List<Song>(new[] { song1, song2, song3 }).IncludeByPropertyName(fsql, "Catetory"); + ---普通属性 where/take/select 参数将无效 + 文档:https://github.com/dotnetcore/FreeSql/wiki/%E8%B4%AA%E5%A9%AA%E5%8A%A0%E8%BD%BD + + + + 选择一个集合或普通属性 + 设置临时的子集合关系映射,格式:子类属性=T1属性,多组以逗号分割 + 设置子集合只取条数 + 设置子集合只查询部分字段 + + - + - Range 要求 Value 应该逗号分割,并且长度为 2 + 查询数据,加工为树型 List 返回 + 注意:实体需要配置父子导航属性 + + + - + - 回滚 + 使用递归 CTE 查询树型的所有子记录,或者所有父记录。 + 通过测试的数据库:MySql8.0、SqlServer、PostgreSQL、Oracle、Sqlite、Firebird、达梦、人大金仓、翰高 + 返回隐藏字段:.ToList(a => new { item = a, level = "a.cte_level", path = "a.cte_path" }) + * v2.0.0 兼容 MySql5.6 向上或向下查询,但不支持 pathSelector/pathSeparator 详细:https://github.com/dotnetcore/FreeSql/issues/536 + + + false(默认):由父级向子级的递归查询true:由子级向父级的递归查询 + 路径内容选择 + 连接路径内容 + 递归层级 + - + - 运行时错误,反射获取 IncludeMany 方法失败 + 随机排序 + 支持:MySql/SqlServer/PostgreSQL/Oracle/Sqlite/Firebird/达梦/金仓/神通 + 不支持:MsAcess + - + - 蛋疼的 Access 插入只能一条一条执行,不支持 values(..),(..) 也不支持 select .. UNION ALL select .. + 插入数据字典 Dictionary<string, object> + + - + - BaseEntity.Initialization 初始化错误,获取到 IFreeSql 是 null + 插入数据字典,传入 Dictionary<string, object> 集合 + + - + - 【{thisName}】Block access and wait for recovery: {exMessage} + 更新数据字典 Dictionary<string, object> + + - + - 无法将 IQueryable<{typeofName}> 转换为 ISelect<{typeofName}>,因为他的实现不是 FreeSql.Extensions.Linq.QueryableProvider + 更新数据字典,传入 Dictionary<string, object> 集合 + + - + - 连接字符串错误 + 插入或更新数据字典,此功能依赖数据库特性(低版本可能不支持),参考如下: + MySql 5.6+: on duplicate key update + PostgreSQL 9.4+: on conflict do update + SqlServer 2008+: merge into + Oracle 11+: merge into + Sqlite: replace into + 达梦: merge into + 人大金仓:on conflict do update + 神通:merge into + MsAccess:不支持 + + - + - 【{thisName}】连接字符串错误,请检查。 + 删除数据字典 Dictionary<string, object> + + - + - 连接字符串错误,或者检查项目属性 > 生成 > 目标平台:x86 | x64,或者改用 FreeSql.Provider.SqliteCore 访问 arm 平台 + 删除数据字典,传入 Dictionary<string, object> 集合 + + - + - 【{thisName}】连接字符串错误,请检查。或者检查项目属性 > 生成 > 目标平台:x86 | x64,或者改用 FreeSql.Provider.SqliteCore 访问 arm 平台 + 原生sql语法条件,Where("col = @xxx", new { xxx = 1 }) + 提示:parms 参数还可以传 Dictionary<string, object> + sql语法条件 + 参数 + - + - FreeSql.Provider.CustomAdapter 无法使用 CreateCommand + 原生sql语法条件,Where("col = @xxx", new { xxx = 1 }) + 提示:parms 参数还可以传 Dictionary<string, object> + sql语法条件 + 参数 + - + - FreeSql.Provider.CustomAdapter 仅支持 UseConnectionFactory 方式构建 IFreeSql + 使用 and 拼接两个 lambda 表达式 + - + - 达梦 CodeFirst 不支持代码创建 tablespace 与 schemas {tbname} + 使用 and 拼接两个 lambda 表达式 + + true 时生效 + + - + - -DB 参数错误,未提供 ConnectionString + 使用 or 拼接两个 lambda 表达式 + - + - -DB 参数错误,格式为:MySql,ConnectionString + 使用 or 拼接两个 lambda 表达式 + + true 时生效 + + - + - -DB 参数错误,不支持的类型:"{dbargs}" + 将 lambda 表达式取反 + + true 时生效 + - + - {method} 是 FreeSql.Provider.{provider} 特有的功能 + 使用 and 拼接两个 lambda 表达式 + - + - fsql.InsertOrUpdate Sqlite 无法完成 UpdateColumns 操作 + 使用 and 拼接两个 lambda 表达式 + + true 时生效 + + - + - 主库 + 使用 or 拼接两个 lambda 表达式 + - + - MygisGeometry.Parse 未实现 "{wkt}" + 使用 or 拼接两个 lambda 表达式 + + true 时生效 + + - + - -NameOptions 参数错误,格式为:0,0,0,0 + 将 lambda 表达式取反 + + true 时生效 + - + - 未实现该功能 + 使用 and 拼接两个 lambda 表达式 + - + - 未实现错误,请反馈给作者 + 使用 and 拼接两个 lambda 表达式 + + true 时生效 + + - + - 找不到 {name} + 使用 or 拼接两个 lambda 表达式 + - + - FreeSql.Provider.{providerName} 未实现 Skip/Offset 功能,如果需要分页请使用判断上一次 id + 使用 or 拼接两个 lambda 表达式 + + true 时生效 + + - + - 对象池 + 将 lambda 表达式取反 + + true 时生效 + - + - 旧表(OldName):{tboldname} 存在,数据库已存在 {tbname} 表,无法改名 + 使用 and 拼接两个 lambda 表达式 + - + - OnConflictDoUpdate 功能要求实体类必须设置 IsPrimary 属性 + 使用 and 拼接两个 lambda 表达式 + + true 时生效 + + - + - Oracle CodeFirst 不支持代码创建 tablespace 与 schemas {tbname} + 使用 or 拼接两个 lambda 表达式 + - + - 解析失败 {callExpMethodName} {message},提示:可以使用扩展方法 IQueryable.RestoreToSelect() 还原为 ISelect 再查询 + 使用 or 拼接两个 lambda 表达式 + + true 时生效 + + - + - fsql.InsertOrUpdate + IfExistsDoNothing + {providerName}要求实体类 {tableCsName} 必须有主键 + 将 lambda 表达式取反 + + true 时生效 + - + - SelectMany 错误的类型:{typeFullName} + 使用 and 拼接两个 lambda 表达式 + - + - 从库 + 使用 and 拼接两个 lambda 表达式 - - - - 类型 {objentityTypeFullName} 不可迁移 - - - - - 类型 {objentityTypeFullName} 不可迁移,可迁移属性0个 - - - - - 未实现 {columnDbTypeTextFull} 类型映射 - - - - - 错误的参数设置:{args} - - - - - {qoteSql} is NULL,除非设置特性 [Column(IsNullable = false)] - - - - - 分表字段值 "{dt}" 不能小于 "{beginTime} " - - - - - 分表字段值不能为 null - - - - - 分表字段值 "{columnValue}" 不能转化成 DateTime - - - - - 分表字段值 "{dt}" 未匹配到分表名 - - - - - T2 类型错误 - - - - - tableName 格式错误,示例:“log_{yyyyMMdd}” - - - - - {Type}.AsType 参数错误,请传入正确的实体类型 - - - - - {thatFullName} 类型无法访问构造函数 - - - - - {name} 类型错误 - - - - - {Type}.AsType 参数不支持指定为 object - - - - - 类型 {typeofFullName} 错误,不能使用 IncludeMany - - - - - 无法解析表达式:{exp} - - - - - 无法解析表达式方法 {exp3tmpCallMethodName} - - - - - 请使用 fsql.InsertDict(dict) 方法插入字典数据 - - - - - 动态构建Class Type - - - - - - 根据字典,创建 table 对应的实体对象 - - - + + true 时生效 + - + - 根据实体对象,创建 table 对应的字典 + 使用 or 拼接两个 lambda 表达式 - - - + - C#: that >= between && that <= and - SQL: that BETWEEN between AND and + 使用 or 拼接两个 lambda 表达式 - - - + + true 时生效 + - + - 注意:这个方法和 Between 有细微区别 - C#: that >= start && that < end - SQL: that >= start and that < end + 将 lambda 表达式取反 - - - + + true 时生效 - + - 获取 Type 的原始 c# 文本表示 + 生成类似Mongodb的ObjectId有序、不重复Guid - - - - - 测量两个经纬度的距离,返回单位:米 - - 经纬坐标1 - 经纬坐标2 - 返回距离(单位:米) - - + - 将 IEnumable<T> 转成 ISelect<T>,以便使用 FreeSql 的查询功能。此方法用于 Lambda 表达式中,快速进行集合导航的查询。 + 插入数据 - - + - + - 多表查询 + 插入数据,传入实体 + + - + - 本方法实现从已知的内存 List 数据,进行和 ISelect.IncludeMany 相同功能的贪婪加载 - 示例:new List<Song>(new[] { song1, song2, song3 }).IncludeMany(fsql, a => a.Tags); - 文档:https://github.com/dotnetcore/FreeSql/wiki/%E8%B4%AA%E5%A9%AA%E5%8A%A0%E8%BD%BD + 插入数据,传入实体数组 - - - - 选择一个集合的导航属性,如: .IncludeMany(a => a.Tags) - 可以 .Where 设置临时的关系映射,如: .IncludeMany(a => a.Tags.Where(tag => tag.TypeId == a.Id)) - 可以 .Take(5) 每个子集合只取5条,如: .IncludeMany(a => a.Tags.Take(5)) - 可以 .Select 设置只查询部分字段,如: (a => new TNavigate { Title = a.Title }) - - 即能 ThenInclude,还可以二次过滤(这个 EFCore 做不到?) + - + - 本方法实现从已知的内存 List 数据,进行和 ISelect.IncludeMany/Include 相同功能的贪婪加载 - 集合:new List<Song>(new[] { song1, song2, song3 }).IncludeByPropertyName(fsql, "Tags", "ParentId=Id", 5, "Id,Name"); - 普通:new List<Song>(new[] { song1, song2, song3 }).IncludeByPropertyName(fsql, "Catetory"); - ---普通属性 where/take/select 参数将无效 - 文档:https://github.com/dotnetcore/FreeSql/wiki/%E8%B4%AA%E5%A9%AA%E5%8A%A0%E8%BD%BD + 插入数据,传入实体集合 - - - 选择一个集合或普通属性 - 设置临时的子集合关系映射,格式:子类属性=T1属性,多组以逗号分割 - 设置子集合只取条数 - 设置子集合只查询部分字段 + - - + - 查询数据,加工为树型 List 返回 - 注意:实体需要配置父子导航属性 + 插入数据,传入实体集合 - + - + - 使用递归 CTE 查询树型的所有子记录,或者所有父记录。 - 通过测试的数据库:MySql8.0、SqlServer、PostgreSQL、Oracle、Sqlite、Firebird、达梦、人大金仓、翰高 - 返回隐藏字段:.ToList(a => new { item = a, level = "a.cte_level", path = "a.cte_path" }) - * v2.0.0 兼容 MySql5.6 向上或向下查询,但不支持 pathSelector/pathSeparator 详细:https://github.com/dotnetcore/FreeSql/issues/536 + 插入或更新数据,此功能依赖数据库特性(低版本可能不支持),参考如下: + MySql 5.6+: on duplicate key update + PostgreSQL 9.4+: on conflict do update + SqlServer 2008+: merge into + Oracle 11+: merge into + Sqlite: replace into + 达梦: merge into + 人大金仓:on conflict do update + 神通:merge into + MsAccess:不支持 + 注意区别:FreeSql.Repository 仓储也有 InsertOrUpdate 方法(不依赖数据库特性) - - false(默认):由父级向子级的递归查询true:由子级向父级的递归查询 - 路径内容选择 - 连接路径内容 - 递归层级 - + - 随机排序 - 支持:MySql/SqlServer/PostgreSQL/Oracle/Sqlite/Firebird/达梦/金仓/神通 - 不支持:MsAcess + 修改数据 + - + - 插入数据字典 Dictionary<string, object> + 修改数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - + + 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - + - 插入数据字典,传入 Dictionary<string, object> 集合 + 查询数据 - + - + - 更新数据字典 Dictionary<string, object> + 查询数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - + + 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - + - 更新数据字典,传入 Dictionary<string, object> 集合 + 删除数据 - + - + - 插入或更新数据字典,此功能依赖数据库特性(低版本可能不支持),参考如下: - MySql 5.6+: on duplicate key update - PostgreSQL 9.4+: on conflict do update - SqlServer 2008+: merge into - Oracle 11+: merge into - Sqlite: replace into - 达梦: merge into - 人大金仓:on conflict do update - 神通:merge into - MsAccess:不支持 + 删除数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - + + 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - + - 删除数据字典 Dictionary<string, object> + 开启事务(不支持异步) + v1.5.0 关闭了线程事务超时自动提交的机制 - - + 事务体 () => {} - + - 删除数据字典,传入 Dictionary<string, object> 集合 + 开启事务(不支持异步) + v1.5.0 关闭了线程事务超时自动提交的机制 - - + + 事务体 () => {} - + - 原生sql语法条件,Where("col = @xxx", new { xxx = 1 }) - 提示:parms 参数还可以传 Dictionary<string, object> + 数据库访问对象 - sql语法条件 - 参数 - - + - 原生sql语法条件,Where("col = @xxx", new { xxx = 1 }) - 提示:parms 参数还可以传 Dictionary<string, object> + 所有拦截方法都在这里 - sql语法条件 - 参数 - - + - 使用 and 拼接两个 lambda 表达式 + CodeFirst 模式开发相关方法 - - + - 使用 and 拼接两个 lambda 表达式 + DbFirst 模式开发相关方法 - - true 时生效 - - - + - 使用 or 拼接两个 lambda 表达式 + 全局过滤设置,可默认附加为 Select/Update/Delete 条件 - - - - 使用 or 拼接两个 lambda 表达式 - - - true 时生效 - - + + + diff --git a/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj b/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj index d14a86433..870b76116 100644 --- a/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj +++ b/Providers/FreeSql.Provider.KingbaseES/FreeSql.Provider.KingbaseES.csproj @@ -20,7 +20,7 @@ - + @@ -31,6 +31,10 @@ Always + + + + diff --git a/Providers/FreeSql.Provider.KingbaseES/KingbaseESAdo/KingbaseESAdo.cs b/Providers/FreeSql.Provider.KingbaseES/KingbaseESAdo/KingbaseESAdo.cs index 167312313..e6f82931d 100644 --- a/Providers/FreeSql.Provider.KingbaseES/KingbaseESAdo/KingbaseESAdo.cs +++ b/Providers/FreeSql.Provider.KingbaseES/KingbaseESAdo/KingbaseESAdo.cs @@ -3,10 +3,13 @@ using FreeSql.Internal.Model; using FreeSql.Internal.ObjectPool; using Kdbndp; +using Newtonsoft.Json.Linq; using System; using System.Collections; +using System.Collections.Generic; using System.Data.Common; using System.Linq; +using System.Text; using System.Threading; namespace FreeSql.KingbaseES @@ -43,9 +46,10 @@ public KingbaseESAdo(CommonUtils util, string masterConnectionString, string[] s public override object AddslashesProcessParam(object param, Type mapType, ColumnInfo mapColumn) { if (param == null) return "NULL"; - if (mapType != null && mapType != param.GetType() && (param is IEnumerable == false)) + if (mapType != null && mapType != param.GetType() && (param is IEnumerable == false || param is JToken || param is JObject || param is JArray)) param = Utils.GetDataReaderValue(mapType, param); + bool isdic; if (param is bool || param is bool?) return (bool)param ? "'t'" : "'f'"; else if (param is string) @@ -62,6 +66,16 @@ public override object AddslashesProcessParam(object param, Type mapType, Column else if (param is DateTime?) return AddslashesTypeHandler(typeof(DateTime?), param) ?? string.Concat("'", ((DateTime)param).ToString("yyyy-MM-dd HH:mm:ss.ffffff"), "'"); +#if net60 + else if (param is DateOnly || param is DateOnly?) + return AddslashesTypeHandler(typeof(DateOnly), param) ?? string.Concat("'", ((DateOnly)param).ToString("yyyy-MM-dd"), "'"); + else if (param is TimeOnly || param is TimeOnly?) + { + var ts = (TimeOnly)param; + return $"'{ts.Hour}:{ts.Minute}:{ts.Second}.{ts.Millisecond}'"; + } +#endif + else if (param is TimeSpan || param is TimeSpan?) { var ts = (TimeSpan)param; @@ -69,6 +83,31 @@ public override object AddslashesProcessParam(object param, Type mapType, Column } else if (param is byte[]) return $"'\\x{CommonUtils.BytesSqlRaw(param as byte[])}'"; + else if (param is JToken || param is JObject || param is JArray) + return string.Concat("'", param.ToString().Replace("'", "''"), "'::jsonb"); + else if ((isdic = param is Dictionary) || + param is IEnumerable>) + { + var pgdics = isdic ? param as Dictionary : + param as IEnumerable>; + + var pghstore = new StringBuilder("'"); + var pairs = pgdics.ToArray(); + + for (var i = 0; i < pairs.Length; i++) + { + if (i != 0) pghstore.Append(","); + + pghstore.AppendFormat("\"{0}\"=>", pairs[i].Key.Replace("'", "''")); + + if (pairs[i].Value == null) + pghstore.Append("NULL"); + else + pghstore.AppendFormat("\"{0}\"", pairs[i].Value.Replace("'", "''")); + } + + return pghstore.Append("'::hstore"); + } else if (param is IEnumerable) return AddslashesIEnumerable(param, mapType, mapColumn); diff --git a/Providers/FreeSql.Provider.KingbaseES/KingbaseESCodeFirst.cs b/Providers/FreeSql.Provider.KingbaseES/KingbaseESCodeFirst.cs index 44c28eb76..a6a0b7eab 100644 --- a/Providers/FreeSql.Provider.KingbaseES/KingbaseESCodeFirst.cs +++ b/Providers/FreeSql.Provider.KingbaseES/KingbaseESCodeFirst.cs @@ -1,11 +1,17 @@ using FreeSql.Internal; using FreeSql.Internal.Model; using KdbndpTypes; +using Newtonsoft.Json.Linq; using System; +using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; +using System.Net; +using System.Net.NetworkInformation; +using System.Numerics; using System.Text; +using System.Text.RegularExpressions; namespace FreeSql.KingbaseES { @@ -36,22 +42,58 @@ public KingbaseESCodeFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpressi { typeof(TimeSpan).FullName, CsToDb.New(KdbndpDbType.Time, "time","time NOT NULL", false, false, 0) },{ typeof(TimeSpan?).FullName, CsToDb.New(KdbndpDbType.Time, "time", "time",false, true, null) }, { typeof(DateTime).FullName, CsToDb.New(KdbndpDbType.Timestamp, "timestamp", "timestamp NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateTime?).FullName, CsToDb.New(KdbndpDbType.Timestamp, "timestamp", "timestamp", false, true, null) }, +#if net60 + { typeof(TimeOnly).FullName, CsToDb.New(KdbndpDbType.Time, "time", "time NOT NULL", false, false, 0) },{ typeof(TimeOnly?).FullName, CsToDb.New(KdbndpDbType.Time, "time", "time", false, true, null) }, + { typeof(DateOnly).FullName, CsToDb.New(KdbndpDbType.Date, "date", "date NOT NULL", false, false, new DateTime(1970,1,1)) },{ typeof(DateOnly?).FullName, CsToDb.New(KdbndpDbType.Date, "date", "date", false, true, null) }, +#endif + { typeof(bool).FullName, CsToDb.New(KdbndpDbType.Boolean, "bool","bool NOT NULL", null, false, false) },{ typeof(bool?).FullName, CsToDb.New(KdbndpDbType.Boolean, "bool","bool", null, true, null) }, { typeof(Byte[]).FullName, CsToDb.New(KdbndpDbType.Bytea, "bytea", "bytea", false, null, new byte[0]) }, + { typeof(BitArray).FullName, CsToDb.New(KdbndpDbType.Varbit, "varbit", "varbit(64)", false, null, new BitArray(new byte[64])) }, + { typeof(BigInteger).FullName, CsToDb.New(KdbndpDbType.Numeric, "numeric", "numeric(78,0) NOT NULL", false, false, 0) },{ typeof(BigInteger?).FullName, CsToDb.New(KdbndpDbType.Numeric, "numeric", "numeric(78,0)", false, true, null) }, + + { typeof(KdbndpPoint).FullName, CsToDb.New(KdbndpDbType.Point, "point", "point NOT NULL", false, false, new KdbndpPoint(0, 0)) },{ typeof(KdbndpPoint?).FullName, CsToDb.New(KdbndpDbType.Point, "point", "point", false, true, null) }, + { typeof(KdbndpLine).FullName, CsToDb.New(KdbndpDbType.Line, "line", "line NOT NULL", false, false, new KdbndpLine(0, 0, 1)) },{ typeof(KdbndpLine?).FullName, CsToDb.New(KdbndpDbType.Line, "line", "line", false, true, null) }, + { typeof(KdbndpLSeg).FullName, CsToDb.New(KdbndpDbType.LSeg, "lseg", "lseg NOT NULL", false, false, new KdbndpLSeg(0, 0, 0, 0)) },{ typeof(KdbndpLSeg?).FullName, CsToDb.New(KdbndpDbType.LSeg, "lseg", "lseg", false, true, null) }, + { typeof(KdbndpBox).FullName, CsToDb.New(KdbndpDbType.Box, "box", "box NOT NULL", false, false, new KdbndpBox(0, 0, 0, 0)) },{ typeof(KdbndpBox?).FullName, CsToDb.New(KdbndpDbType.Box, "box", "box", false, true, null) }, + { typeof(KdbndpPath).FullName, CsToDb.New(KdbndpDbType.Path, "path", "path NOT NULL", false, false, new KdbndpPath(new KdbndpPoint(0, 0))) },{ typeof(KdbndpPath?).FullName, CsToDb.New(KdbndpDbType.Path, "path", "path", false, true, null) }, + { typeof(KdbndpPolygon).FullName, CsToDb.New(KdbndpDbType.Polygon, "polygon", "polygon NOT NULL", false, false, new KdbndpPolygon(new KdbndpPoint(0, 0), new KdbndpPoint(0, 0))) },{ typeof(KdbndpPolygon?).FullName, CsToDb.New(KdbndpDbType.Polygon, "polygon", "polygon", false, true, null) }, + { typeof(KdbndpCircle).FullName, CsToDb.New(KdbndpDbType.Circle, "circle", "circle NOT NULL", false, false, new KdbndpCircle(0, 0, 0)) },{ typeof(KdbndpCircle?).FullName, CsToDb.New(KdbndpDbType.Circle, "circle", "circle", false, true, null) }, + + { typeof((IPAddress Address, int Subnet)).FullName, CsToDb.New(KdbndpDbType.Cidr, "cidr", "cidr NOT NULL", false, false, (IPAddress.Any, 0)) },{ typeof((IPAddress Address, int Subnet)?).FullName, CsToDb.New(KdbndpDbType.Cidr, "cidr", "cidr", false, true, null) }, + { typeof(IPAddress).FullName, CsToDb.New(KdbndpDbType.Inet, "inet", "inet", false, null, IPAddress.Any) }, + { typeof(PhysicalAddress).FullName, CsToDb.New(KdbndpDbType.MacAddr, "macaddr", "macaddr", false, null, PhysicalAddress.None) }, + + { typeof(JToken).FullName, CsToDb.New(KdbndpDbType.Jsonb, "jsonb", "jsonb", false, null, JToken.Parse("{}")) }, + { typeof(JObject).FullName, CsToDb.New(KdbndpDbType.Jsonb, "jsonb", "jsonb", false, null, JObject.Parse("{}")) }, + { typeof(JArray).FullName, CsToDb.New(KdbndpDbType.Jsonb, "jsonb", "jsonb", false, null, JArray.Parse("[]")) }, { typeof(Guid).FullName, CsToDb.New(KdbndpDbType.Uuid, "uuid", "uuid NOT NULL", false, false, Guid.Empty) },{ typeof(Guid?).FullName, CsToDb.New(KdbndpDbType.Uuid, "uuid", "uuid", false, true, null) }, + + { typeof(KdbndpRange).FullName, CsToDb.New(KdbndpDbType.Range | KdbndpDbType.Integer, "int4range", "int4range NOT NULL", false, false, KdbndpRange.Empty) },{ typeof(KdbndpRange?).FullName, CsToDb.New(KdbndpDbType.Range | KdbndpDbType.Integer, "int4range", "int4range", false, true, null) }, + { typeof(KdbndpRange).FullName, CsToDb.New(KdbndpDbType.Range | KdbndpDbType.Bigint, "int8range", "int8range NOT NULL", false, false, KdbndpRange.Empty) },{ typeof(KdbndpRange?).FullName, CsToDb.New(KdbndpDbType.Range | KdbndpDbType.Bigint, "int8range", "int8range", false, true, null) }, + { typeof(KdbndpRange).FullName, CsToDb.New(KdbndpDbType.Range | KdbndpDbType.Numeric, "numrange", "numrange NOT NULL", false, false, KdbndpRange.Empty) },{ typeof(KdbndpRange?).FullName, CsToDb.New(KdbndpDbType.Range | KdbndpDbType.Numeric, "numrange", "numrange", false, true, null) }, + { typeof(KdbndpRange).FullName, CsToDb.New(KdbndpDbType.Range | KdbndpDbType.Timestamp, "tsrange", "tsrange NOT NULL", false, false, KdbndpRange.Empty) },{ typeof(KdbndpRange?).FullName, CsToDb.New(KdbndpDbType.Range | KdbndpDbType.Timestamp, "tsrange", "tsrange", false, true, null) }, + + { typeof(Dictionary).FullName, CsToDb.New(KdbndpDbType.Hstore, "hstore", "hstore", false, null, new Dictionary()) }, }; public override DbInfoResult GetDbInfo(Type type) { - if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return new DbInfoResult((int)trydc.type, trydc.dbtype, trydc.dbtypeFull, trydc.isnullable, trydc.defaultValue); + var isarray = type.FullName != "System.Byte[]" && type.IsArray; + var elementType = isarray ? type.GetElementType() : type; + var info = GetDbInfoNoneArray(elementType); + if (info == null) return null; + if (isarray == false) return new DbInfoResult((int)info.type, info.dbtype, info.dbtypeFull, info.isnullable, info.defaultValue); + var dbtypefull = Regex.Replace(info.dbtypeFull, $@"{info.dbtype}(\s*\([^\)]+\))?", "$0[]").Replace(" NOT NULL", ""); + return new DbInfoResult((int)(info.type | KdbndpDbType.Array), $"{info.dbtype}[]", dbtypefull, null, Array.CreateInstance(elementType, 0)); + } + CsToDb GetDbInfoNoneArray(Type type) + { + if (_dicCsToDb.TryGetValue(type.FullName, out var trydc)) return trydc; if (type.IsArray) return null; var enumType = type.IsEnum ? type : null; - if (enumType == null && type.IsNullableType()) - { - var genericTypes = type.GetGenericArguments(); - if (genericTypes.Length == 1 && genericTypes.First().IsEnum) enumType = genericTypes.First(); - } + if (enumType == null && type.IsNullableType() && type.GenericTypeArguments.Length == 1 && type.GenericTypeArguments.First().IsEnum) enumType = type.GenericTypeArguments.First(); if (enumType != null) { var newItem = enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ? @@ -65,7 +107,7 @@ public override DbInfoResult GetDbInfo(Type type) _dicCsToDb.Add(type.FullName, newItem); } } - return new DbInfoResult((int)newItem.type, newItem.dbtype, newItem.dbtypeFull, newItem.isnullable, newItem.defaultValue); + return newItem; } return null; } diff --git a/Providers/FreeSql.Provider.KingbaseES/KingbaseESDbFirst.cs b/Providers/FreeSql.Provider.KingbaseES/KingbaseESDbFirst.cs index b3a23d6cb..20f0a4379 100644 --- a/Providers/FreeSql.Provider.KingbaseES/KingbaseESDbFirst.cs +++ b/Providers/FreeSql.Provider.KingbaseES/KingbaseESDbFirst.cs @@ -2,12 +2,16 @@ using FreeSql.Internal; using FreeSql.Internal.Model; using KdbndpTypes; +using Newtonsoft.Json.Linq; using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Linq; +using System.Net; +using System.Net.NetworkInformation; using System.Text; using System.Text.RegularExpressions; @@ -25,75 +29,175 @@ public KingbaseESDbFirst(IFreeSql orm, CommonUtils commonUtils, CommonExpression _commonExpression = commonExpression; } - public int GetDbType(DbColumnInfo column) => (int)GetSqlDbType(column); - KdbndpDbType GetSqlDbType(DbColumnInfo column) + public int GetDbType(DbColumnInfo column) => (int)GetKdbndpDbType(column); + KdbndpDbType GetKdbndpDbType(DbColumnInfo column) { var dbtype = column.DbTypeText; var isarray = dbtype?.EndsWith("[]") == true; if (isarray) dbtype = dbtype.Remove(dbtype.Length - 2); - var ret = KdbndpDbType.Unknown; + KdbndpDbType ret = KdbndpDbType.Unknown; switch (dbtype?.ToLower().TrimStart('_')) { - case "tinyint": ret = KdbndpDbType.Smallint; break; + case "smallint": case "int2": ret = KdbndpDbType.Smallint; break; + case "integer": case "int4": ret = KdbndpDbType.Integer; break; + case "bigint": case "int8": ret = KdbndpDbType.Bigint; break; case "numeric": ret = KdbndpDbType.Numeric; break; + case "real": case "float4": ret = KdbndpDbType.Real; break; + case "double precision": case "float8": ret = KdbndpDbType.Double; break; case "money": ret = KdbndpDbType.Money; break; - case "char": ret = column.MaxLength == 36 ? KdbndpDbType.Uuid : KdbndpDbType.Char; break; case "bpchar": ret = KdbndpDbType.Char; break; + case "character varying": case "varchar": ret = KdbndpDbType.Varchar; break; case "text": ret = KdbndpDbType.Text; break; - case "datetime": ret = KdbndpDbType.Timestamp; break; case "timestamp": ret = KdbndpDbType.Timestamp; break; - case "timestamptz": ret = KdbndpDbType.Timestamp; break; + case "timestamptz": ret = KdbndpDbType.TimestampTz; break; case "date": ret = KdbndpDbType.Date; break; case "time": ret = KdbndpDbType.Time; break; - case "timetz": ret = KdbndpDbType.Time; break; - case "interval": ret = KdbndpDbType.Time; break; + case "timetz": ret = KdbndpDbType.TimeTz; break; + case "interval": ret = KdbndpDbType.Interval; break; case "bool": ret = KdbndpDbType.Boolean; break; - case "blob": ret = KdbndpDbType.Bytea; break; case "bytea": ret = KdbndpDbType.Bytea; break; case "bit": ret = KdbndpDbType.Bit; break; case "varbit": ret = KdbndpDbType.Varbit; break; + case "point": ret = KdbndpDbType.Point; break; + case "line": ret = KdbndpDbType.Line; break; + case "lseg": ret = KdbndpDbType.LSeg; break; + case "box": ret = KdbndpDbType.Box; break; + case "path": ret = KdbndpDbType.Path; break; + case "polygon": ret = KdbndpDbType.Polygon; break; + case "circle": ret = KdbndpDbType.Circle; break; + + case "cidr": ret = KdbndpDbType.Cidr; break; + case "inet": ret = KdbndpDbType.Inet; break; + case "macaddr": ret = KdbndpDbType.MacAddr; break; + + case "json": ret = KdbndpDbType.Json; break; + case "jsonb": ret = KdbndpDbType.Jsonb; break; case "uuid": ret = KdbndpDbType.Uuid; break; + + case "int4range": ret = KdbndpDbType.Range | KdbndpDbType.Integer; break; + case "int8range": ret = KdbndpDbType.Range | KdbndpDbType.Bigint; break; + case "numrange": ret = KdbndpDbType.Range | KdbndpDbType.Numeric; break; + case "tsrange": ret = KdbndpDbType.Range | KdbndpDbType.Timestamp; break; + case "tstzrange": ret = KdbndpDbType.Range | KdbndpDbType.TimestampTz; break; + case "daterange": ret = KdbndpDbType.Range | KdbndpDbType.Date; break; + + case "hstore": ret = KdbndpDbType.Hstore; break; + case "geometry": ret = KdbndpDbType.Geometry; break; } - return ret; + return isarray ? (ret | KdbndpDbType.Array) : ret; } - static ConcurrentDictionary _dicDbToCs = new ConcurrentDictionary(); - static KingbaseESDbFirst() - { - var defaultDbToCs = new Dictionary() { - { (int)KdbndpDbType.Smallint, new DbToCs("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(int), typeof(int?), "{0}.Value", "GetInt16") }, - { (int)KdbndpDbType.Integer, new DbToCs("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(long), typeof(long?), "{0}.Value", "GetInt32") }, - { (int)KdbndpDbType.Bigint, new DbToCs("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") }, - { (int)KdbndpDbType.Real, new DbToCs("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") }, - { (int)KdbndpDbType.Double, new DbToCs("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") }, - { (int)KdbndpDbType.Numeric, new DbToCs("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") }, - - { (int)KdbndpDbType.Char, new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, - { (int)KdbndpDbType.Varchar, new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, - { (int)KdbndpDbType.Text, new DbToCs("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, - - { (int)KdbndpDbType.Timestamp, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") }, - { (int)KdbndpDbType.Date, new DbToCs("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") }, - { (int)KdbndpDbType.Time, new DbToCs("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") }, - - { (int)KdbndpDbType.Boolean, new DbToCs("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") }, - { (int)KdbndpDbType.Bytea, new DbToCs("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") }, - - { (int)KdbndpDbType.Uuid, new DbToCs("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid", typeof(Guid), typeof(Guid?), "{0}", "GetString") }, + static readonly Dictionary _dicDbToCs = new Dictionary() { + { (int)KdbndpDbType.Smallint, ("(short?)", "short.Parse({0})", "{0}.ToString()", "short?", typeof(short), typeof(short?), "{0}.Value", "GetInt16") }, + { (int)KdbndpDbType.Integer, ("(int?)", "int.Parse({0})", "{0}.ToString()", "int?", typeof(int), typeof(int?), "{0}.Value", "GetInt32") }, + { (int)KdbndpDbType.Bigint, ("(long?)", "long.Parse({0})", "{0}.ToString()", "long?", typeof(long), typeof(long?), "{0}.Value", "GetInt64") }, + { (int)KdbndpDbType.Numeric, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") }, + { (int)KdbndpDbType.Real, ("(float?)", "float.Parse({0})", "{0}.ToString()", "float?", typeof(float), typeof(float?), "{0}.Value", "GetFloat") }, + { (int)KdbndpDbType.Double, ("(double?)", "double.Parse({0})", "{0}.ToString()", "double?", typeof(double), typeof(double?), "{0}.Value", "GetDouble") }, + { (int)KdbndpDbType.Money, ("(decimal?)", "decimal.Parse({0})", "{0}.ToString()", "decimal?", typeof(decimal), typeof(decimal?), "{0}.Value", "GetDecimal") }, + + { (int)KdbndpDbType.Char, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, + { (int)KdbndpDbType.Varchar, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, + { (int)KdbndpDbType.Text, ("", "{0}.Replace(StringifySplit, \"|\")", "{0}.Replace(\"|\", StringifySplit)", "string", typeof(string), typeof(string), "{0}", "GetString") }, + + { (int)KdbndpDbType.Timestamp, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") }, + { (int)KdbndpDbType.TimestampTz, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") }, + { (int)KdbndpDbType.Date, ("(DateTime?)", "new DateTime(long.Parse({0}))", "{0}.Ticks.ToString()", "DateTime?", typeof(DateTime), typeof(DateTime?), "{0}.Value", "GetDateTime") }, + { (int)KdbndpDbType.Time, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") }, + { (int)KdbndpDbType.TimeTz, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") }, + { (int)KdbndpDbType.Interval, ("(TimeSpan?)", "TimeSpan.Parse(double.Parse({0}))", "{0}.Ticks.ToString()", "TimeSpan?", typeof(TimeSpan), typeof(TimeSpan?), "{0}.Value", "GetValue") }, + + { (int)KdbndpDbType.Boolean, ("(bool?)", "{0} == \"1\"", "{0} == true ? \"1\" : \"0\"", "bool?", typeof(bool), typeof(bool?), "{0}.Value", "GetBoolean") }, + { (int)KdbndpDbType.Bytea, ("(byte[])", "Convert.FromBase64String({0})", "Convert.ToBase64String({0})", "byte[]", typeof(byte[]), typeof(byte[]), "{0}", "GetValue") }, + { (int)KdbndpDbType.Bit, ("(BitArray)", "{0}.ToBitArray()", "{0}.To1010()", "BitArray", typeof(BitArray), typeof(BitArray), "{0}", "GetValue") }, + { (int)KdbndpDbType.Varbit, ("(BitArray)", "{0}.ToBitArray()", "{0}.To1010()", "BitArray", typeof(BitArray), typeof(BitArray), "{0}", "GetValue") }, + + { (int)KdbndpDbType.Point, ("(KdbndpPoint?)", "KdbndpPoint.Parse({0})", "{0}.ToString()", "KdbndpPoint", typeof(KdbndpPoint), typeof(KdbndpPoint?), "{0}", "GetValue") }, + { (int)KdbndpDbType.Line, ("(KdbndpLine?)", "KdbndpLine.Parse({0})", "{0}.ToString()", "KdbndpLine", typeof(KdbndpLine), typeof(KdbndpLine?), "{0}", "GetValue") }, + { (int)KdbndpDbType.LSeg, ("(KdbndpLSeg?)", "KdbndpLSeg.Parse({0})", "{0}.ToString()", "KdbndpLSeg", typeof(KdbndpLSeg), typeof(KdbndpLSeg?), "{0}", "GetValue") }, + { (int)KdbndpDbType.Box, ("(KdbndpBox?)", "KdbndpBox.Parse({0})", "{0}.ToString()", "KdbndpBox", typeof(KdbndpBox), typeof(KdbndpBox?), "{0}", "GetValue") }, + { (int)KdbndpDbType.Path, ("(KdbndpPath?)", "KdbndpPath.Parse({0})", "{0}.ToString()", "KdbndpPath", typeof(KdbndpPath), typeof(KdbndpPath?), "{0}", "GetValue") }, + { (int)KdbndpDbType.Polygon, ("(KdbndpPolygon?)", "KdbndpPolygon.Parse({0})", "{0}.ToString()", "KdbndpPolygon", typeof(KdbndpPolygon), typeof(KdbndpPolygon?), "{0}", "GetValue") }, + { (int)KdbndpDbType.Circle, ("(KdbndpCircle?)", "KdbndpCircle.Parse({0})", "{0}.ToString()", "KdbndpCircle", typeof(KdbndpCircle), typeof(KdbndpCircle?), "{0}", "GetValue") }, + + { (int)KdbndpDbType.Cidr, ("((IPAddress, int)?)", "(IPAddress, int)({0})", "{0}.ToString()", "(IPAddress, int)", typeof((IPAddress, int)), typeof((IPAddress, int)?), "{0}", "GetValue") }, + { (int)KdbndpDbType.Inet, ("(IPAddress)", "IPAddress.Parse({0})", "{0}.ToString()", "IPAddress", typeof(IPAddress), typeof(IPAddress), "{0}", "GetValue") }, + { (int)KdbndpDbType.MacAddr, ("(PhysicalAddress?)", "PhysicalAddress.Parse({0})", "{0}.ToString()", "PhysicalAddress", typeof(PhysicalAddress), typeof(PhysicalAddress), "{0}", "GetValue") }, + + { (int)KdbndpDbType.Json, ("(JToken)", "JToken.Parse({0})", "{0}.ToString()", "JToken", typeof(JToken), typeof(JToken), "{0}", "GetString") }, + { (int)KdbndpDbType.Jsonb, ("(JToken)", "JToken.Parse({0})", "{0}.ToString()", "JToken", typeof(JToken), typeof(JToken), "{0}", "GetString") }, + { (int)KdbndpDbType.Uuid, ("(Guid?)", "Guid.Parse({0})", "{0}.ToString()", "Guid", typeof(Guid), typeof(Guid?), "{0}", "GetString") }, + + { (int)(KdbndpDbType.Range | KdbndpDbType.Integer), ("(KdbndpRange?)", "{0}.ToKdbndpRange()", "{0}.ToString()", "KdbndpRange", typeof(KdbndpRange), typeof(KdbndpRange?), "{0}", "GetString") }, + { (int)(KdbndpDbType.Range | KdbndpDbType.Bigint), ("(KdbndpRange?)", "{0}.ToKdbndpRange()", "{0}.ToString()", "KdbndpRange", typeof(KdbndpRange), typeof(KdbndpRange?), "{0}", "GetString") }, + { (int)(KdbndpDbType.Range | KdbndpDbType.Numeric), ("(KdbndpRange?)", "{0}.ToKdbndpRange()", "{0}.ToString()", "KdbndpRange", typeof(KdbndpRange), typeof(KdbndpRange?), "{0}", "GetString") }, + { (int)(KdbndpDbType.Range | KdbndpDbType.Timestamp), ("(KdbndpRange?)", "{0}.ToKdbndpRange()", "{0}.ToString()", "KdbndpRange", typeof(KdbndpRange), typeof(KdbndpRange?), "{0}", "GetString") }, + { (int)(KdbndpDbType.Range | KdbndpDbType.TimestampTz), ("(KdbndpRange?)", "{0}.ToKdbndpRange()", "{0}.ToString()", "KdbndpRange", typeof(KdbndpRange), typeof(KdbndpRange?), "{0}", "GetString") }, + { (int)(KdbndpDbType.Range | KdbndpDbType.Date), ("(KdbndpRange?)", "{0}.ToKdbndpRange()", "{0}.ToString()", "KdbndpRange", typeof(KdbndpRange), typeof(KdbndpRange?), "{0}", "GetString") }, + + { (int)KdbndpDbType.Hstore, ("(Dictionary)", "JsonConvert.DeserializeObject>({0})", "JsonConvert.SerializeObject({0})", "Dictionary", typeof(Dictionary), typeof(Dictionary), "{0}", "GetValue") }, + + /*** array ***/ + + { (int)(KdbndpDbType.Smallint | KdbndpDbType.Array), ("(short[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "short[]", typeof(short[]), typeof(short[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Integer | KdbndpDbType.Array), ("(int[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "int[]", typeof(int[]), typeof(int[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Bigint | KdbndpDbType.Array), ("(long[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "long[]", typeof(long[]), typeof(long[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Numeric | KdbndpDbType.Array), ("(decimal[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "decimal[]", typeof(decimal[]), typeof(decimal[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Real | KdbndpDbType.Array), ("(float[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "float[]", typeof(float[]), typeof(float[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Double | KdbndpDbType.Array), ("(double[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "double[]", typeof(double[]), typeof(double[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Money | KdbndpDbType.Array), ("(decimal[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "decimal[]", typeof(decimal[]), typeof(decimal[]), "{0}", "GetValue") }, + + { (int)(KdbndpDbType.Char | KdbndpDbType.Array), ("(string[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Varchar | KdbndpDbType.Array), ("(string[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Text | KdbndpDbType.Array), ("(string[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "string[]", typeof(string[]), typeof(string[]), "{0}", "GetValue") }, + + { (int)(KdbndpDbType.Timestamp | KdbndpDbType.Array), ("(DateTime[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.TimestampTz | KdbndpDbType.Array), ("(DateTime[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Date | KdbndpDbType.Array), ("(DateTime[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "DateTime[]", typeof(DateTime[]), typeof(DateTime[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Time | KdbndpDbType.Array), ("(TimeSpan[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.TimeTz | KdbndpDbType.Array), ("(TimeSpan[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Interval | KdbndpDbType.Array), ("(TimeSpan[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "TimeSpan[]", typeof(TimeSpan[]), typeof(TimeSpan[]), "{0}", "GetValue") }, + + { (int)(KdbndpDbType.Boolean | KdbndpDbType.Array), ("(bool[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "bool[]", typeof(bool[]), typeof(bool[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Bytea | KdbndpDbType.Array), ("(byte[][])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "byte[][]", typeof(byte[][]), typeof(byte[][]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Bit | KdbndpDbType.Array), ("(BitArray[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "BitArray[]", typeof(BitArray[]), typeof(BitArray[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Varbit | KdbndpDbType.Array), ("(BitArray[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "BitArray[]", typeof(BitArray[]), typeof(BitArray[]), "{0}", "GetValue") }, + + { (int)(KdbndpDbType.Point | KdbndpDbType.Array), ("(KdbndpPoint[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "KdbndpPoint[]", typeof(KdbndpPoint[]), typeof(KdbndpPoint[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Line | KdbndpDbType.Array), ("(KdbndpLine[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "KdbndpLine[]", typeof(KdbndpLine[]), typeof(KdbndpLine[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.LSeg | KdbndpDbType.Array), ("(KdbndpLSeg[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "KdbndpLSeg[]", typeof(KdbndpLSeg[]), typeof(KdbndpLSeg[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Box | KdbndpDbType.Array), ("(KdbndpBox[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "KdbndpBox[]", typeof(KdbndpBox[]), typeof(KdbndpBox[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Path | KdbndpDbType.Array), ("(KdbndpPath[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "KdbndpPath[]", typeof(KdbndpPath[]), typeof(KdbndpPath[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Polygon | KdbndpDbType.Array), ("(KdbndpPolygon[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "KdbndpPolygon[]", typeof(KdbndpPolygon[]), typeof(KdbndpPolygon[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Circle | KdbndpDbType.Array), ("(KdbndpCircle[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "KdbndpCircle[]", typeof(KdbndpCircle[]), typeof(KdbndpCircle[]), "{0}", "GetValue") }, + + { (int)(KdbndpDbType.Cidr | KdbndpDbType.Array), ("((IPAddress, int)[])", "JsonConvert.DeserializeObject<(IPAddress, int)[]>({0})", "JsonConvert.SerializeObject({0})", "(IPAddress, int)[]", typeof((IPAddress, int)[]), typeof((IPAddress, int)[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Inet | KdbndpDbType.Array), ("(IPAddress[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "IPAddress[]", typeof(IPAddress[]), typeof(IPAddress[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.MacAddr | KdbndpDbType.Array), ("(PhysicalAddress[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "PhysicalAddress[]", typeof(PhysicalAddress[]), typeof(PhysicalAddress[]), "{0}", "GetValue") }, + + { (int)(KdbndpDbType.Json | KdbndpDbType.Array), ("(JToken[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "JToken[]", typeof(JToken[]), typeof(JToken[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Jsonb | KdbndpDbType.Array), ("(JToken[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "JToken[]", typeof(JToken[]), typeof(JToken[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Uuid | KdbndpDbType.Array), ("(Guid[])", "JsonConvert.DeserializeObject({0})", "JsonConvert.SerializeObject({0})", "Guid[]", typeof(Guid[]), typeof(Guid[]), "{0}", "GetValue") }, + + { (int)(KdbndpDbType.Range | KdbndpDbType.Integer | KdbndpDbType.Array), ("(KdbndpRange[])", "JsonConvert.DeserializeObject[]>({0})", "JsonConvert.SerializeObject({0})", "KdbndpRange[]", typeof(KdbndpRange[]), typeof(KdbndpRange[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Range | KdbndpDbType.Bigint | KdbndpDbType.Array), ("(KdbndpRange[])", "JsonConvert.DeserializeObject[]>({0})", "JsonConvert.SerializeObject({0})", "KdbndpRange[]", typeof(KdbndpRange[]), typeof(KdbndpRange[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Range | KdbndpDbType.Numeric | KdbndpDbType.Array), ("(KdbndpRange[])", "JsonConvert.DeserializeObject[]>({0})", "JsonConvert.SerializeObject({0})", "KdbndpRange[]", typeof(KdbndpRange[]), typeof(KdbndpRange[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Range | KdbndpDbType.Timestamp | KdbndpDbType.Array), ("(KdbndpRange[])", "JsonConvert.DeserializeObject[]>({0})", "JsonConvert.SerializeObject({0})", "KdbndpRange[]", typeof(KdbndpRange[]), typeof(KdbndpRange[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Range | KdbndpDbType.TimestampTz | KdbndpDbType.Array), ("(KdbndpRange[])", "JsonConvert.DeserializeObject[]>({0})", "JsonConvert.SerializeObject({0})", "KdbndpRange[]", typeof(KdbndpRange[]), typeof(KdbndpRange[]), "{0}", "GetValue") }, + { (int)(KdbndpDbType.Range | KdbndpDbType.Date | KdbndpDbType.Array), ("(KdbndpRange[])", "JsonConvert.DeserializeObject[]>({0})", "JsonConvert.SerializeObject({0})", "KdbndpRange[]", typeof(KdbndpRange[]), typeof(KdbndpRange[]), "{0}", "GetValue") }, + + { (int)(KdbndpDbType.Hstore | KdbndpDbType.Array), ("(Dictionary[])", "JsonConvert.DeserializeObject[]>({0})", "JsonConvert.SerializeObject({0})", "Dictionary[]", typeof(Dictionary[]), typeof(Dictionary[]), "{0}", "GetValue") }, }; - foreach (var kv in defaultDbToCs) - _dicDbToCs.TryAdd(kv.Key, kv.Value); - } public string GetCsConvert(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? (column.IsNullable ? trydc.csConvert : trydc.csConvert.Replace("?", "")) : null; public string GetCsParse(DbColumnInfo column) => _dicDbToCs.TryGetValue(column.DbType, out var trydc) ? trydc.csParse : null; diff --git a/Providers/FreeSql.Provider.KingbaseES/KingbaseESExpression.cs b/Providers/FreeSql.Provider.KingbaseES/KingbaseESExpression.cs index bfeab3819..9ee054050 100644 --- a/Providers/FreeSql.Provider.KingbaseES/KingbaseESExpression.cs +++ b/Providers/FreeSql.Provider.KingbaseES/KingbaseESExpression.cs @@ -1,4 +1,5 @@ using FreeSql.Internal; +using Newtonsoft.Json.Linq; using System; using System.Collections; using System.Collections.Generic; @@ -98,10 +99,10 @@ public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) var enumStr = ExpressionLambdaToSql(callExp.Object, tsc); tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType); return enumStr; - } - var value = ExpressionGetValue(callExp.Object, out var success); - if (success) return formatSql(value, typeof(string), null, null); - return callExp.Arguments.Count == 0 ? $"({getExp(callExp.Object)})::text" : null; + } + var value = ExpressionGetValue(callExp.Object, out var success); + if (success) return formatSql(value, typeof(string), null, null); + return callExp.Arguments.Count == 0 ? $"({getExp(callExp.Object)})::text" : null; } return null; } @@ -131,6 +132,36 @@ public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) if (objType != null || objType.IsArrayOrList()) { string left = null; + switch (objType.FullName) + { + case "Newtonsoft.Json.Linq.JToken": + case "Newtonsoft.Json.Linq.JObject": + case "Newtonsoft.Json.Linq.JArray": + left = objExp == null ? null : getExp(objExp); + switch (callExp.Method.Name) + { + case "get_Item": return $"{left}->{getExp(callExp.Arguments[argIndex])}"; + case "Any": return $"(jsonb_array_length(coalesce({left},'[]')) > 0)"; + case "Contains": + var json = getExp(callExp.Arguments[argIndex]); + if (objType == typeof(JArray)) + return $"(coalesce({left},'[]') ? ({json})::text)"; + if (json.StartsWith("'") && json.EndsWith("'")) + return $"(coalesce({left},'{{}}') @> {_common.FormatSql("{0}", JToken.Parse(json.Trim('\'')))})"; + return $"(coalesce({left},'{{}}') @> ({json})::jsonb)"; + case "ContainsKey": return $"(coalesce({left},'{{}}') ? {getExp(callExp.Arguments[argIndex])})"; + case "Concat": + var right2 = getExp(callExp.Arguments[argIndex]); + return $"(coalesce({left},'{{}}') || {right2})"; + case "LongCount": + case "Count": return $"jsonb_array_length(coalesce({left},'[]'))"; + case "Parse": + var json2 = getExp(callExp.Arguments[argIndex]); + if (json2.StartsWith("'") && json2.EndsWith("'")) return _common.FormatSql("{0}", JToken.Parse(json2.Trim('\''))); + return $"({json2})::jsonb"; + } + break; + } if (objType == typeof(Dictionary)) { left = objExp == null ? null : getExp(objExp); @@ -165,14 +196,14 @@ public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) tsc.isNotSetMapColumnTmp = false; tsc.SetMapColumnTmp(null).SetMapTypeReturnOld(oldMapType); if (oldDbParams != null) tsc.SetDbParamsReturnOld(oldDbParams); - //判断 in 或 array @> array - if (left.StartsWith("array[") && left.EndsWith("]")) - return $"({args1}) in ({left.Substring(6, left.Length - 7)})"; - if (left.StartsWith("(") && left.EndsWith(")")) //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格 - return $"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})"; - if (args1.StartsWith("(") && args1.EndsWith(")")) args1 = $"array[{args1.TrimStart('(').TrimEnd(')')}]"; - else args1 = $"array[{args1}]"; - if (objExp != null) + //判断 in 或 array @> array + if (left.StartsWith("array[") && left.EndsWith("]")) + return $"({args1}) in ({left.Substring(6, left.Length - 7)})"; + if (left.StartsWith("(") && left.EndsWith(")")) //在各大 Provider AdoProvider 中已约定,500元素分割, 3空格\r\n4空格 + return $"(({args1}) in {left.Replace(", \r\n \r\n", $") \r\n OR ({args1}) in (")})"; + if (args1.StartsWith("(") && args1.EndsWith(")")) args1 = $"array[{args1.TrimStart('(').TrimEnd(')')}]"; + else args1 = $"array[{args1}]"; + if (objExp != null) { var dbinfo = _common._orm.CodeFirst.GetDbInfo(objExp.Type); if (dbinfo != null) args1 = $"{args1}::{dbinfo.dbtype}"; @@ -210,6 +241,28 @@ public override string ExpressionLambdaToSqlOther(Expression exp, ExpTSC tsc) case "Count": return $"case when {left} is null then 0 else array_length({left},1) end"; } } + switch (memParentExp.FullName) + { + case "Newtonsoft.Json.Linq.JToken": + case "Newtonsoft.Json.Linq.JObject": + case "Newtonsoft.Json.Linq.JArray": + var left = getExp(memExp.Expression); + switch (memExp.Member.Name) + { + case "Count": return $"jsonb_array_length(coalesce({left},'[]'))"; + } + break; + } + if (memParentExp == typeof(Dictionary)) + { + var left = getExp(memExp.Expression); + switch (memExp.Member.Name) + { + case "Count": return $"case when {left} is null then 0 else array_length(akeys({left}),1) end"; + case "Keys": return $"akeys({left})"; + case "Values": return $"avals({left})"; + } + } } break; case ExpressionType.NewArrayInit: @@ -316,7 +369,7 @@ public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, return _common.StringConcat(concatNewArrExp.Expressions.Select(a => getExp(a)).ToArray(), null); return _common.StringConcat(exp.Arguments.Select(a => getExp(a)).ToArray(), null); case "Format": - if (exp.Arguments[0].NodeType != ExpressionType.Constant) throw new Exception(CoreStrings.Not_Implemented_Expression_ParameterUseConstant(exp,exp.Arguments[0])); + if (exp.Arguments[0].NodeType != ExpressionType.Constant) throw new Exception(CoreStrings.Not_Implemented_Expression_ParameterUseConstant(exp, exp.Arguments[0])); var expArgsHack = exp.Arguments.Count == 2 && exp.Arguments[1].NodeType == ExpressionType.NewArrayInit ? (exp.Arguments[1] as NewArrayExpression).Expressions : exp.Arguments.Where((a, z) => z > 0); //3个 {} 时,Arguments 解析出来是分开的 @@ -395,6 +448,8 @@ public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, if (exp.Method.Name == "TrimStart") return $"ltrim({left})"; if (exp.Method.Name == "TrimEnd") return $"rtrim({left})"; } + var trimArg1 = ""; + var trimArg2 = ""; foreach (var argsTrim02 in exp.Arguments) { var argsTrim01s = new[] { argsTrim02 }; @@ -405,11 +460,14 @@ public override string ExpressionLambdaToSqlCallString(MethodCallExpression exp, } foreach (var argsTrim01 in argsTrim01s) { - if (exp.Method.Name == "Trim") left = $"trim(both {getExp(argsTrim01)} from {left})"; - if (exp.Method.Name == "TrimStart") left = $"ltrim({left},{getExp(argsTrim01)})"; - if (exp.Method.Name == "TrimEnd") left = $"rtrim({left},{getExp(argsTrim01)})"; + var trimChr = getExp(argsTrim01).Trim('\''); + if (trimChr.Length == 1) trimArg1 += trimChr; + else trimArg2 += $" || ({trimChr})"; } } + if (exp.Method.Name == "Trim") left = $"trim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})"; + if (exp.Method.Name == "TrimStart") left = $"ltrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})"; + if (exp.Method.Name == "TrimEnd") left = $"rtrim({left}, {_common.FormatSql("{0}", trimArg1)}{trimArg2})"; return left; case "Replace": return $"replace({left}, {getExp(exp.Arguments[0])}, {getExp(exp.Arguments[1])})"; case "CompareTo": return $"case when {left} = {getExp(exp.Arguments[0])} then 0 when {left} > {getExp(exp.Arguments[0])} then 1 else -1 end"; diff --git a/Providers/FreeSql.Provider.KingbaseES/KingbaseESExtensions.cs b/Providers/FreeSql.Provider.KingbaseES/KingbaseESExtensions.cs index 407ba4965..372ba1116 100644 --- a/Providers/FreeSql.Provider.KingbaseES/KingbaseESExtensions.cs +++ b/Providers/FreeSql.Provider.KingbaseES/KingbaseESExtensions.cs @@ -3,6 +3,7 @@ using FreeSql.Internal.Model; using Kdbndp; using System; +using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; @@ -20,6 +21,13 @@ public static partial class FreeSqlKingbaseESGlobalExtensions public static string FormatKingbaseES(this string that, params object[] args) => _kingbaseesAdo.Addslashes(that, args); static FreeSql.KingbaseES.KingbaseESAdo _kingbaseesAdo = new FreeSql.KingbaseES.KingbaseESAdo(); + internal static string To1010(this BitArray ba) + { + char[] ret = new char[ba.Length]; + for (int a = 0; a < ba.Length; a++) ret[a] = ba[a] ? '1' : '0'; + return new string(ret); + } + #region ExecuteKdbCopy /// /// 批量插入或更新(操作的字段数量超过 2000 时收益大) diff --git a/Providers/FreeSql.Provider.KingbaseES/KingbaseESProvider.cs b/Providers/FreeSql.Provider.KingbaseES/KingbaseESProvider.cs index 76a7dde86..e8ca58570 100644 --- a/Providers/FreeSql.Provider.KingbaseES/KingbaseESProvider.cs +++ b/Providers/FreeSql.Provider.KingbaseES/KingbaseESProvider.cs @@ -1,6 +1,17 @@ -using FreeSql.Internal.CommonProvider; +using FreeSql.Internal; +using FreeSql.Internal.CommonProvider; +using KdbndpTypes; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using System; +using System.Collections; +using System.Collections.Generic; using System.Data.Common; +using System.Linq.Expressions; +using System.Net; +using System.Net.NetworkInformation; +using System.Numerics; +using System.Reflection; using System.Threading; namespace FreeSql.KingbaseES @@ -8,6 +19,73 @@ namespace FreeSql.KingbaseES public class KingbaseESProvider : BaseDbProvider, IFreeSql { + static int _firstInit = 1; + static void InitInternal() + { + if (Interlocked.Exchange(ref _firstInit, 0) == 1) //不能放在 static ctor .NetFramework 可能报初始化类型错误 + { + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(BigInteger)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(BitArray)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(KdbndpPoint)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(KdbndpLine)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(KdbndpLSeg)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(KdbndpBox)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(KdbndpPath)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(KdbndpPolygon)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(KdbndpCircle)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof((IPAddress Address, int Subnet))] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(IPAddress)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(PhysicalAddress)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(KdbndpRange)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(KdbndpRange)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(KdbndpRange)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(KdbndpRange)] = true; + + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(Dictionary)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JToken)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JObject)] = true; + Utils.dicExecuteArrayRowReadClassOrTuple[typeof(JArray)] = true; + + var MethodJTokenFromObject = typeof(JToken).GetMethod("FromObject", new[] { typeof(object) }); + var MethodJObjectFromObject = typeof(JObject).GetMethod("FromObject", new[] { typeof(object) }); + var MethodJArrayFromObject = typeof(JArray).GetMethod("FromObject", new[] { typeof(object) }); + var MethodJTokenParse = typeof(JToken).GetMethod("Parse", new[] { typeof(string) }); + var MethodJObjectParse = typeof(JObject).GetMethod("Parse", new[] { typeof(string) }); + var MethodJArrayParse = typeof(JArray).GetMethod("Parse", new[] { typeof(string) }); + var MethodJsonConvertDeserializeObject = typeof(JsonConvert).GetMethod("DeserializeObject", new[] { typeof(string), typeof(Type) }); + var MethodToString = typeof(Utils).GetMethod("ToStringConcat", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(object) }, null); + Utils.GetDataReaderValueBlockExpressionSwitchTypeFullName.Add((LabelTarget returnTarget, Expression valueExp, Type type) => + { + switch (type.FullName) + { + case "Newtonsoft.Json.Linq.JToken": + return Expression.IfThenElse( + Expression.TypeIs(valueExp, typeof(string)), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJTokenParse, Expression.Convert(valueExp, typeof(string))), typeof(JToken))), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJTokenFromObject, valueExp), typeof(JToken)))); + case "Newtonsoft.Json.Linq.JObject": + return Expression.IfThenElse( + Expression.TypeIs(valueExp, typeof(string)), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJObjectParse, Expression.Convert(valueExp, typeof(string))), typeof(JObject))), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJObjectFromObject, valueExp), typeof(JObject)))); + case "Newtonsoft.Json.Linq.JArray": + return Expression.IfThenElse( + Expression.TypeIs(valueExp, typeof(string)), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJArrayParse, Expression.Convert(valueExp, typeof(string))), typeof(JArray))), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJArrayFromObject, valueExp), typeof(JArray)))); + } + if (typeof(IList).IsAssignableFrom(type)) + return Expression.IfThenElse( + Expression.TypeIs(valueExp, typeof(string)), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJsonConvertDeserializeObject, Expression.Convert(valueExp, typeof(string)), Expression.Constant(type, typeof(Type))), type)), + Expression.Return(returnTarget, Expression.TypeAs(Expression.Call(MethodJsonConvertDeserializeObject, Expression.Convert(Expression.Call(MethodToString, valueExp), typeof(string)), Expression.Constant(type, typeof(Type))), type))); + return null; + }); + + Select0Provider._dicMethodDataReaderGetValue[typeof(Guid)] = typeof(DbDataReader).GetMethod("GetGuid", new Type[] { typeof(int) }); + } + } + public override ISelect CreateSelectProvider(object dywhere) => new KingbaseESSelect(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); public override IInsert CreateInsertProvider() => new KingbaseESInsert(this, this.InternalCommonUtils, this.InternalCommonExpression); public override IUpdate CreateUpdateProvider(object dywhere) => new KingbaseESUpdate(this, this.InternalCommonUtils, this.InternalCommonExpression, dywhere); @@ -16,6 +94,7 @@ public class KingbaseESProvider : BaseDbProvider, IFreeSql public KingbaseESProvider(string masterConnectionString, string[] slaveConnectionString, Func connectionFactory = null) { + InitInternal(); this.InternalCommonUtils = new KingbaseESUtils(this); this.InternalCommonExpression = new KingbaseESExpression(this.InternalCommonUtils); diff --git a/Providers/FreeSql.Provider.KingbaseES/KingbaseESUtils.cs b/Providers/FreeSql.Provider.KingbaseES/KingbaseESUtils.cs index cb9eaf0a8..2e54223d6 100644 --- a/Providers/FreeSql.Provider.KingbaseES/KingbaseESUtils.cs +++ b/Providers/FreeSql.Provider.KingbaseES/KingbaseESUtils.cs @@ -2,11 +2,15 @@ using FreeSql.Internal.Model; using Kdbndp; using KdbndpTypes; +using Newtonsoft.Json.Linq; using System; +using System.Collections; using System.Collections.Generic; using System.Data.Common; using System.Globalization; using System.Linq; +using System.Net; +using System.Numerics; using System.Text; namespace FreeSql.KingbaseES @@ -31,32 +35,55 @@ static Array getParamterArrayValue(Type arrayType, object value, object defaultV return ret; } static Dictionary> dicGetParamterValue = new Dictionary> { + { typeof(JToken).FullName, a => string.Concat(a) }, { typeof(JToken[]).FullName, a => getParamterArrayValue(typeof(string), a, null) }, + { typeof(JObject).FullName, a => string.Concat(a) }, { typeof(JObject[]).FullName, a => getParamterArrayValue(typeof(string), a, null) }, + { typeof(JArray).FullName, a => string.Concat(a) }, { typeof(JArray[]).FullName, a => getParamterArrayValue(typeof(string), a, null) }, { typeof(uint).FullName, a => long.Parse(string.Concat(a)) }, { typeof(uint[]).FullName, a => getParamterArrayValue(typeof(long), a, 0) }, { typeof(uint?[]).FullName, a => getParamterArrayValue(typeof(long?), a, null) }, { typeof(ulong).FullName, a => decimal.Parse(string.Concat(a)) }, { typeof(ulong[]).FullName, a => getParamterArrayValue(typeof(decimal), a, 0) }, { typeof(ulong?[]).FullName, a => getParamterArrayValue(typeof(decimal?), a, null) }, { typeof(ushort).FullName, a => int.Parse(string.Concat(a)) }, { typeof(ushort[]).FullName, a => getParamterArrayValue(typeof(int), a, 0) }, { typeof(ushort?[]).FullName, a => getParamterArrayValue(typeof(int?), a, null) }, { typeof(byte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(byte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(byte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) }, { typeof(sbyte).FullName, a => short.Parse(string.Concat(a)) }, { typeof(sbyte[]).FullName, a => getParamterArrayValue(typeof(short), a, 0) }, { typeof(sbyte?[]).FullName, a => getParamterArrayValue(typeof(short?), a, null) }, { typeof(char).FullName, a => string.Concat(a).Replace('\0', ' ').ToCharArray().FirstOrDefault() }, + { typeof(BigInteger).FullName, a => BigInteger.Parse(string.Concat(a), System.Globalization.NumberStyles.Any) }, { typeof(BigInteger[]).FullName, a => getParamterArrayValue(typeof(BigInteger), a, 0) }, { typeof(BigInteger?[]).FullName, a => getParamterArrayValue(typeof(BigInteger?), a, null) }, + + { typeof(KdbndpPath).FullName, a => { + var path = (KdbndpPath)a; + try { int count = path.Count; return path; } catch { return new KdbndpPath(new KdbndpPoint(0, 0)); } + } }, + { typeof(KdbndpPath[]).FullName, a => getParamterArrayValue(typeof(KdbndpPath), a, new KdbndpPath(new KdbndpPoint(0, 0))) }, + { typeof(KdbndpPath?[]).FullName, a => getParamterArrayValue(typeof(KdbndpPath?), a, null) }, + + { typeof(KdbndpPolygon).FullName, a => { + var polygon = (KdbndpPolygon)a; + try { int count = polygon.Count; return polygon; } catch { return new KdbndpPolygon(new KdbndpPoint(0, 0), new KdbndpPoint(0, 0)); } + } }, + { typeof(KdbndpPolygon[]).FullName, a => getParamterArrayValue(typeof(KdbndpPolygon), a, new KdbndpPolygon(new KdbndpPoint(0, 0), new KdbndpPoint(0, 0))) }, + { typeof(KdbndpPolygon?[]).FullName, a => getParamterArrayValue(typeof(KdbndpPolygon?), a, null) }, + + { typeof((IPAddress Address, int Subnet)).FullName, a => { + var inet = ((IPAddress Address, int Subnet))a; + if (inet.Address == null) return (IPAddress.Any, inet.Subnet); + return inet; + } }, + { typeof((IPAddress Address, int Subnet)[]).FullName, a => getParamterArrayValue(typeof((IPAddress Address, int Subnet)), a, (IPAddress.Any, 0)) }, + { typeof((IPAddress Address, int Subnet)?[]).FullName, a => getParamterArrayValue(typeof((IPAddress Address, int Subnet)?), a, null) }, }; static object getParamterValue(Type type, object value, int level = 0) { if (type.FullName == "System.Byte[]") return value; + if (type.FullName == "System.Char[]") return value; if (type.IsArray && level == 0) { var elementType = type.GetElementType(); Type enumType = null; if (elementType.IsEnum) enumType = elementType; - else if (elementType.IsNullableType()) - { - var genericTypesFirst = elementType.GetGenericArguments().First(); - if (genericTypesFirst.IsEnum) enumType = genericTypesFirst; - } + else if (elementType.IsNullableType() && elementType.GenericTypeArguments.First().IsEnum) enumType = elementType.GenericTypeArguments.First(); if (enumType != null) return enumType.GetCustomAttributes(typeof(FlagsAttribute), false).Any() ? getParamterArrayValue(typeof(long), value, elementType.IsEnum ? null : enumType.CreateInstanceGetDefaultValue()) : getParamterArrayValue(typeof(int), value, elementType.IsEnum ? null : enumType.CreateInstanceGetDefaultValue()); return dicGetParamterValue.TryGetValue(type.FullName, out var trydicarr) ? trydicarr(value) : value; } - if (type.IsNullableType()) type = type.GetGenericArguments().First(); + if (type.IsNullableType()) type = type.GenericTypeArguments.First(); if (type.IsEnum) return (int)value; if (dicGetParamterValue.TryGetValue(type.FullName, out var trydic)) return trydic(value); return value; @@ -72,6 +99,17 @@ public override DbParameter AppendParamter(List _params, string par //} else { var tp = _orm.CodeFirst.GetDbInfo(type)?.type; if (tp != null) ret.KdbndpDbType = (KdbndpDbType)tp.Value; + if (col != null) + { + var dbtype = (KdbndpDbType)_orm.DbFirst.GetDbType(new DatabaseModel.DbColumnInfo { DbTypeText = col.DbTypeText }); + if (dbtype != KdbndpDbType.Unknown) + { + ret.KdbndpDbType = dbtype; + //if (col.DbSize != 0) ret.Size = col.DbSize; + if (col.DbPrecision != 0) ret.Precision = col.DbPrecision; + if (col.DbScale != 0) ret.Scale = col.DbScale; + } + } //} _params?.Add(ret); return ret; @@ -148,6 +186,20 @@ public override string GetNoneParamaterSqlValue(List specialParams, if (dbinfo != null) sb.Append("::").Append(dbinfo.dbtype); return sb.ToString(); } + else if (type2 == typeof(BitArray)) + { + return $"'{(value as BitArray).To1010()}'"; + } + else if (type2 == typeof(KdbndpLine) || type2 == typeof(KdbndpLine?)) + { + var line = value.ToString(); + return line == "{0,0,0}" ? "'{0,-1,-1}'" : $"'{line}'"; + } + else if (type2 == typeof((IPAddress Address, int Subnet)) || type2 == typeof((IPAddress Address, int Subnet)?)) + { + var cidr = ((IPAddress Address, int Subnet))value; + return $"'{cidr.Address}/{cidr.Subnet}'"; + } else if (dicGetParamterValue.ContainsKey(type2.FullName)) { value = string.Concat(value);