From 7f0bf3cd0191f1033cc87b7c423b263d501f1598 Mon Sep 17 00:00:00 2001 From: 2881099 <2881099@qq.com> Date: Mon, 11 Dec 2023 23:38:09 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E4=BC=98=E5=8C=96=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=88=86=E8=A1=A8=20join=20=E5=88=86=E8=A1=A8=EF=BC=88?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E8=B4=B4=E5=90=88=EF=BC=89=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Examples/base_entity/ModAsTableImpl.cs | 10 +- Examples/base_entity/Program.cs | 50 +- FreeSql/DataAnnotations/TableAttribute.cs | 95 +- FreeSql/FreeSql.xml | 1070 +++++++++-------- .../Internal/CommonProvider/DeleteProvider.cs | 4 +- .../SelectProvider/Select0Provider.cs | 79 +- .../Internal/CommonProvider/UpdateProvider.cs | 4 +- 7 files changed, 717 insertions(+), 595 deletions(-) diff --git a/Examples/base_entity/ModAsTableImpl.cs b/Examples/base_entity/ModAsTableImpl.cs index 295ab4ed..3e00c6eb 100644 --- a/Examples/base_entity/ModAsTableImpl.cs +++ b/Examples/base_entity/ModAsTableImpl.cs @@ -34,14 +34,14 @@ class ModAsTableImpl : IAsTable throw new NotImplementedException(); } - public string[] GetTableNamesBySqlWhere(string sqlWhere, List dbParams, SelectTableInfo tb, CommonUtils commonUtils) + public IAsTableTableNameRangeResult GetTableNamesBySqlWhere(string sqlWhere, List dbParams, SelectTableInfo tb, CommonUtils commonUtils) { var match = Regex.Match(sqlWhere, @"/\*astable\([^\)]+\)*\/"); - if (match.Success == false) return AllTables; + if (match.Success == false) return new IAsTableTableNameRangeResult(AllTables, null, null); var tables = match.Groups[1].Value.Split(',').Where(a => AllTables.Contains(a)).ToArray(); - if (tables.Any() == false) return AllTables; - return tables; - } + if (tables.Any() == false) return new IAsTableTableNameRangeResult(AllTables, null, null); + return new IAsTableTableNameRangeResult(tables, null, null); + } public IAsTable SetDefaultAllTables(Func audit) { diff --git a/Examples/base_entity/Program.cs b/Examples/base_entity/Program.cs index 50038ad4..07018199 100644 --- a/Examples/base_entity/Program.cs +++ b/Examples/base_entity/Program.cs @@ -116,9 +116,17 @@ namespace base_entity public string msg { get; set; } public DateTime createtime { get; set; } public int click { get; set; } - } + } + [Table(Name = "as_table_logext_{yyyyMMddHH}", AsTable = "createtime=2022-1-1 11(12,2 month)")] + class AsTableLogExt + { + public Guid id { get; set; } + public string msg { get; set; } + public DateTime createtime { get; set; } + public int click { get; set; } + } - public class SomeEntity + public class SomeEntity { [Column(IsIdentity = true)] public int Id { get; set; } @@ -619,13 +627,37 @@ namespace base_entity fsql.CodeFirst.GetTableByEntity(typeof(AsTableLog)).AsTableImpl .SetTableName(0, "AsTableLog1") .SetTableName(1, "AsTableLog2") - //.SetDefaultAllTables(value => - //{ - // if (value.Length > 3) return value.Take(3).ToArray(); - // return value; - //}) + .SetDefaultAllTables(value => + { + if (value.Length > 3) return value.Take(3).ToArray(); + return value; + }) ; + + var astsql01 = fsql.Select() + .InnerJoin((a, b) => a.id == b.Id) + .OrderBy((a, b) => a.createtime) + .ToSql(); + + var astsql02 = fsql.Select() + .InnerJoin((a, b) => a.Id == b.id) + .OrderBy((a, b) => b.createtime) + .ToSql(); + + var astsql03 = fsql.Select() + .Where(a => a.createtime < DateTime.Parse("2023-5-1")) + .FromQuery(fsql.Select()) + .InnerJoin((a, b) => a.id == b.id) + .OrderBy((a, b) => a.createtime) + .ToSql(); + + var astsql04 = fsql.Select() + .InnerJoin((a, b) => a.id == b.id) + .Where((a,b) => a.createtime < DateTime.Parse("2023-5-1")) + .OrderBy((a, b) => a.createtime) + .ToSql(); + var testitems = new[] { new AsTableLog{ msg = "msg01", createtime = DateTime.Parse("2022-1-1 13:00:11"), click = 1 }, @@ -1010,10 +1042,6 @@ namespace base_entity var sqlastable2 = fsql.Update(101).AsTable("current_detail_230501").Set(t => t.StatuId, 1).ToSql(); var sqlastable3 = fsql.Delete(101).AsTable("current_detail_230501").ToSql(); - var astsql = fsql.Select() - .InnerJoin((a, b) => a.id == b.Id) - .OrderBy((a, b) => a.createtime) - .ToSql(); diff --git a/FreeSql/DataAnnotations/TableAttribute.cs b/FreeSql/DataAnnotations/TableAttribute.cs index 3499b8d9..b09f870e 100644 --- a/FreeSql/DataAnnotations/TableAttribute.cs +++ b/FreeSql/DataAnnotations/TableAttribute.cs @@ -89,9 +89,21 @@ namespace FreeSql.DataAnnotations IAsTable SetTableName(int index, string tableName); string GetTableNameByColumnValue(object columnValue, bool autoExpand = false); string[] GetTableNamesByColumnValueRange(object columnValue1, object columnValue2); - string[] GetTableNamesBySqlWhere(string sqlWhere, List dbParams, SelectTableInfo tb, CommonUtils commonUtils); + IAsTableTableNameRangeResult GetTableNamesBySqlWhere(string sqlWhere, List dbParams, SelectTableInfo tb, CommonUtils commonUtils); IAsTable SetDefaultAllTables(Func audit); } + public class IAsTableTableNameRangeResult + { + public string[] Names { get; } + public object ColumnValue1 { get; } + public object ColumnValue2 { get; } + public IAsTableTableNameRangeResult(string[] names, object columnValue1, object columnValue2) + { + Names = names; + ColumnValue1 = columnValue1; + ColumnValue2 = columnValue2; + } + } class DateTimeAsTableImpl : IAsTable { readonly object _lock = new object(); @@ -135,6 +147,17 @@ namespace FreeSql.DataAnnotations } } } + public NativeTuple GetRangeByTableName(string tableName) + { + lock (_lock) + { + var idx = _allTables.FindIndex(a => a == tableName); + if (idx == -1) return null; + if (idx == 0) return NativeTuple.Create(_allTablesTime[idx], DateTime.Now); + if (idx == _allTables.Count - 1) return NativeTuple.Create(DateTime.MinValue, _allTablesTime[idx]); + return NativeTuple.Create(_allTablesTime[idx], _allTablesTime[idx - 1]); + } + } DateTime ParseColumnValue(object columnValue) { if (columnValue == null) throw new Exception(CoreStrings.SubTableFieldValue_IsNotNull); @@ -262,9 +285,9 @@ namespace FreeSql.DataAnnotations /// /// /// - public string[] GetTableNamesBySqlWhere(string sqlWhere, List dbParams, SelectTableInfo tb, CommonUtils commonUtils) + public IAsTableTableNameRangeResult GetTableNamesBySqlWhere(string sqlWhere, List dbParams, SelectTableInfo tb, CommonUtils commonUtils) { - if (string.IsNullOrWhiteSpace(sqlWhere)) return _GetDefaultAllTables?.Invoke(AllTables) ?? AllTables; + if (string.IsNullOrWhiteSpace(sqlWhere)) return new IAsTableTableNameRangeResult(_GetDefaultAllTables?.Invoke(AllTables) ?? AllTables, _beginTime, _lastTime); var quoteParameterName = ""; if (commonUtils._orm.Ado.DataType == DataType.ClickHouse) quoteParameterName = "@"; //特殊处理 Clickhouse 参数化 else quoteParameterName = commonUtils.QuoteParamterName(""); @@ -291,7 +314,7 @@ namespace FreeSql.DataAnnotations var val1 = LocalGetParamValue(m.Groups[1].Value); var val2 = LocalGetParamValue(m.Groups[2].Value); if (val1 == null || val2 == null) throw new Exception(CoreStrings.Failed_SubTable_FieldValue(sqlWhere)); - return GetTableNamesByColumnValueRange(val1, val2); + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(val1, val2), ParseColumnValue(val1), ParseColumnValue(val2)); } m = regs[8].Match(newSqlWhere); if (m.Success) @@ -299,23 +322,23 @@ namespace FreeSql.DataAnnotations var val1 = LocalGetParamValue(m.Groups[2].Value); var val2 = LocalGetParamValue(m.Groups[4].Value); if (val1 == null || val2 == null) throw new Exception(CoreStrings.Failed_SubTable_FieldValue(sqlWhere)); - return LocalGetTables(m.Groups[1].Value, m.Groups[3].Value, ParseColumnValue(val1), ParseColumnValue(val2)); + return LocalGetTables(m.Groups[1].Value, m.Groups[3].Value, ParseColumnValue(val1), ParseColumnValue(val2)); } m = regs[10].Match(newSqlWhere); if (m.Success) { var val1 = LocalGetParamValue(m.Groups[2].Value); if (val1 == null) throw new Exception(CoreStrings.Failed_SubTable_FieldValue(sqlWhere)); - return LocalGetTables2(m.Groups[1].Value, ParseColumnValue(val1)); + return LocalGetTables2(m.Groups[1].Value, ParseColumnValue(val1)); } - return _GetDefaultAllTables?.Invoke(AllTables) ?? AllTables; + return new IAsTableTableNameRangeResult(_GetDefaultAllTables?.Invoke(AllTables) ?? AllTables, _beginTime, _lastTime); - object LocalGetParamValue(string paramName) + object LocalGetParamValue(string paramName) { if (dictParams.TryGetValue(quoteParameterName + paramName, out var trydictVal)) return trydictVal; - return dbParams.Where(a => a.ParameterName.Trim(quoteParameterNameCharArray) == m.Groups[2].Value).FirstOrDefault()?.Value; + return dbParams.Where(a => a.ParameterName.Trim(quoteParameterNameCharArray) == paramName).FirstOrDefault()?.Value; } - string[] LocalGetTables(string opt1, string opt2, DateTime val1, DateTime val2) + IAsTableTableNameRangeResult LocalGetTables(string opt1, string opt2, DateTime val1, DateTime val2) { switch (opt1) { @@ -326,15 +349,15 @@ namespace FreeSql.DataAnnotations { case "<": val2 = val2.AddSeconds(-1); - return GetTableNamesByColumnValueRange(_beginTime, val1 > val2 ? val2 : val1); + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(_beginTime, val1 > val2 ? val2 : val1), _beginTime, val1 > val2 ? val2 : val1); case "<=": - return GetTableNamesByColumnValueRange(_beginTime, val1 > val2 ? val2 : val1); - case ">": + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(_beginTime, val1 > val2 ? val2 : val1), _beginTime, val1 > val2 ? val2 : val1); + case ">": val2 = val2.AddSeconds(1); - return GetTableNamesByColumnValueRange(val2, val1); + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(val2, val1), val2, val1); case ">=": - return GetTableNamesByColumnValueRange(val2, val1); - } + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(val2, val1), val2, val1); + } break; case ">": case ">=": @@ -343,38 +366,38 @@ namespace FreeSql.DataAnnotations { case "<": val2 = val2.AddSeconds(-1); - return GetTableNamesByColumnValueRange(val1, val2); + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(val1, val2), val1, val2); case "<=": - return GetTableNamesByColumnValueRange(val1, val2); - case ">": + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(val1, val2), val1, val2); + case ">": val2 = val2.AddSeconds(1); - return GetTableNamesByColumnValueRange(val1 > val2 ? val1 : val2, _lastTime); + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(val1 > val2 ? val1 : val2, _lastTime), val1 > val2 ? val1 : val2, _lastTime); case ">=": - return GetTableNamesByColumnValueRange(val1 > val2 ? val1 : val2, _lastTime); - } + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(val1 > val2 ? val1 : val2, _lastTime), val1 > val2 ? val1 : val2, _lastTime); + } break; } - return _GetDefaultAllTables?.Invoke(AllTables) ?? AllTables; - } - string[] LocalGetTables2(string opt, DateTime val1) + return new IAsTableTableNameRangeResult(_GetDefaultAllTables?.Invoke(AllTables) ?? AllTables, _beginTime, _lastTime); + } + IAsTableTableNameRangeResult LocalGetTables2(string opt, DateTime val1) { switch (m.Groups[1].Value) { case "=": - return GetTableNamesByColumnValueRange(val1, val1); + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(val1, val1), val1, val1); case "<": val1 = val1.AddSeconds(-1); - return GetTableNamesByColumnValueRange(_beginTime, val1); - case "<=": - return GetTableNamesByColumnValueRange(_beginTime, val1); - case ">": + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(_beginTime, val1), _beginTime, val1); + case "<=": + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(_beginTime, val1), _beginTime, val1); + case ">": val1 = val1.AddSeconds(1); - return GetTableNamesByColumnValueRange(val1, _lastTime); - case ">=": - return GetTableNamesByColumnValueRange(val1, _lastTime); - } - return _GetDefaultAllTables?.Invoke(AllTables) ?? AllTables; - } + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(val1, _lastTime), val1, _lastTime); + case ">=": + return new IAsTableTableNameRangeResult(GetTableNamesByColumnValueRange(val1, _lastTime), val1, _lastTime); + } + return new IAsTableTableNameRangeResult(_GetDefaultAllTables?.Invoke(AllTables) ?? AllTables, _beginTime, _lastTime); + } } public string[] AllTables diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index e4fdd619..eb656d99 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1104,53 +1104,52 @@ - + - 获取实体的主键值,以 "*|_,[,_|*" 分割,当任意一个主键属性无值时,返回 null + 动态创建实体类型 - - - - 当Guid无值时,会生成有序的新值 - + + + + 配置Class + + 类名 + 类标记的特性[Table(Name = "xxx")] [Index(xxxx)] - + - 获取实体的主键值,多个主键返回数组 + 配置属性 - - - + 属性名称 + 属性类型 + 属性标记的特性-支持多个 - + - 获取实体的属性值 + 配置属性 - - - - + 属性名称 + 属性类型 + 该属性是否重写父类属性 + 属性标记的特性-支持多个 - + - 获取实体的所有数据,以 (1, 2, xxx) 的形式 + 配置属性 - - - + 属性名称 + 属性类型 + 该属性是否重写父类属性 + 属性默认值 + 属性标记的特性-支持多个 - + - 使用新实体的值,复盖旧实体的值 - - - - - 使用新实体的主键值,复盖旧实体的主键值 + 体的主键值,复盖旧实体的主键值 @@ -1347,6 +1346,113 @@ + mary> + + + + + count() over(order by ...) + + + + + + sum(..) over(order by ...) + + + + + + + avg(..) over(order by ...) + + + + + + max(..) over(order by ...) + + + + + + + + min(..) over(order by ...) + + + + + + + + SqlServer row_number() over(order by ...) + + + + + + isnull、ifnull、coalesce、nvl + + + + + + + + + count(distinct name) + + + + + + + + 注意:使用者自己承担【注入风险】 + + + + + + + 大于 > + + + + + + 大于或等于 >= + + + + + + 小于 < + + + + + + 小于或等于 <= + + + + + + value1 IS NULL + + + + + + + + case when .. then .. end + + + + case when .. then .. end @@ -1425,75 +1531,7 @@ 数据库连接对象创建器 提供者的类型,一般不需要指定,如果一直提示“缺少 FreeSql 数据库实现包:FreeSql.Provider.MySql.dll,可前往 nuget 下载”的错误,说明反射获取不到类型,此时该参数可排上用场例如:typeof(FreeSql.SqlServer.SqlServerProvider<>) - - - - 【开发环境必备】自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改 - 注意:生产环境中谨慎使用 - - true:运行时检查自动同步结构, false:不同步结构(默认) - - - - - 将数据库的主键、自增、索引设置导入,适用 DbFirst 模式,无须在实体类型上设置 [Column(IsPrimary)] 或者 ConfigEntity。此功能目前可用于 mysql/sqlserver/postgresql/oracle。 - 本功能会影响 IFreeSql 首次访问的速度。 - 若使用 CodeFirst 创建索引后,又直接在数据库上建了索引,若无本功能下一次 CodeFirst 迁移时数据库上创建的索引将被删除 - - - - - - - 不使用命令参数化执行,针对 Insert/Update,也可临时使用 IInsert/IUpdate.NoneParameter() - - - - - - - 是否生成命令参数化执行,针对 lambda 表达式解析 - 注意:常量不会参数化,变量才会做参数化 - var id = 100; - fsql.Select<T>().Where(a => a.id == id) 会参数化 - fsql.Select<T>().Where(a => a.id == 100) 不会参数化 - - - - - - - 延时加载导航属性对象,导航属性需要声明 virtual - - - - - - - 监视数据库命令对象 - - 执行前 - 执行后,可监视执行性能 - - - - - 实体类名 -> 数据库表名,命名转换(类名、属性名都生效) - 优先级小于 [Column(Name = "xxx")] - - - - - - - SQL名称是否使用 [] `` "" - true: SELECT .. FROM [table] - false: SELECT .. FROM table - - - - - + 指定映射优先级(从小到大) 例如表名:实体类名 < Aop < FluentApi < Attribute < AsTable @@ -1662,6 +1700,95 @@ 指定事务对象 + am> + + + + + 传入实体,将主键作为条件 + + 实体 + + + + + 传入实体集合,将主键作为条件 + + 实体集合 + + + + + 传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} + + 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 + 是否标识为NOT + + + + + 动态过滤条件 + + + + + + + 禁用全局过滤功能,不传参数时将禁用所有 + + 零个或多个过滤器名字 + + + + + 设置表名规则,可用于分库/分表,参数1:默认表名;返回值:新表名; + + + + + + + 设置表名 + + + + + + + 动态Type,在使用 Delete<object> 后使用本方法,指定实体类型 + + + + + + + 返回即将执行的SQL语句 + + + + + + 执行SQL语句,返回影响的行数 + + + + + + 执行SQL语句,返回被删除的记录 + 注意:此方法只有 Postgresql/SqlServer 有效果 + + + + + + 指定事务对象 + + + + + + + 指定事务对象 @@ -2659,70 +2786,7 @@ - 按列排序,OrderBy(true, a => a.Time) - - - true 时生效 - - - - - - 按列排序,OrderByIf(true, a => a.Time) - - - true 时生效 - - true: DESC, false: ASC - - - - - 按列倒向排序,OrderByDescending(a => a.Time) - - 列 - - - - - 按列倒向排序,OrderByDescending(true, a => a.Time) - - true 时生效 - 列 - - - - - 贪婪加载导航属性,如果查询中已经使用了 a.Parent.Parent 类似表达式,则可以无需此操作 - - - 选择一个导航属性 - - - - - 贪婪加载导航属性,如果查询中已经使用了 a.Parent.Parent 类似表达式,则可以无需此操作 - - - true 时生效 - 选择一个导航属性 - - - - - 贪婪加载集合的导航属性,其实是分两次查询,ToList 后进行了数据重装 - 文档: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 做不到?) - - - + 按列排序,Or 按属性名字符串进行 Include/IncludeMany 操作 @@ -2800,6 +2864,77 @@ lambda表达式 + + + 按属性名字符串进行 Include/IncludeMany 操作 + + true 时生效 + + + + + + 实现 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) @@ -3923,215 +4058,205 @@ + 小写 + + + - 执行的 SQL + 获取数据库枚举类型int值 + + + + + + + 获取c#转换,(int)、(long) + + + + + + + 获取c#值 + + + + + + + 获取c#类型,int、long + + + + + + + 获取c#类型对象 + + + + + + + 获取ado.net读取方法, GetBoolean、GetInt64 + + + + + + + 序列化 + + + + + + + 反序列化 + + + + + + + 获取数据库枚举类型,适用 PostgreSQL + + + + + + + 临时 LambdaExpression.Parameter - + - 参数化命令 + 如果实体类有自增属性,分成两个 List,有值的Item1 merge,无值的Item2 insert + + + + + + + AsType, Ctor, ClearData 三处地方需要重新加载 - + - 状态数据,可与 CurdAfter 共享 + AsType, Ctor, ClearData 三处地方需要重新加载 - + - 发生的错误 + 动态读取 DescriptionAttribute 注释文本 + + + + + + + 通过属性的注释文本,通过 xml 读取 + + + Dict:key=属性名,value=注释 + + + + 更新实体的元数据 - + - 执行SQL命令,返回的结果 + 执行更新的 SQL - + - 耗时(单位:Ticks) + 执行更新命令的参数 - + - 耗时(单位:毫秒) + 执行更新命令影响的行 - + - 标识符,可将 SyncStructureBeforeEventArgs 与 SyncStructureAfterEventArgs 进行匹配 + 更新的实体数量 - + - 实体类型 + 更新的实体 - + - 状态数据,可与 SyncStructureAfter 共享 + 映射优先级,默认: Attribute > FluentApi > Aop - + - 执行的 SQL + 实体特性 + [Table(Name = "tabname")] + [Column(Name = "table_id")] - + - 发生的错误 + 流式接口 + fsql.CodeFirst.ConfigEntity(a => a.Name("tabname")) + fsql.CodeFirst.ConfigEntity(a => a.Property(b => b.Id).Name("table_id")) - + - 耗时(单位:Ticks) + 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"; - + - 耗时(单位:毫秒) + 不进行任何处理 - + - 类型 + 将帕斯卡命名字符串转换为下划线分隔字符串 + + BigApple -> Big_Apple - + - 属性列的元数据 + 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全大写 + + BigApple -> BIG_APPLE - + - 反射的属性信息 + 将帕斯卡命名字符串转换为下划线分隔字符串,且转换为全小写 + + BigApple -> big_apple - + - 获取实体的属性值,也可以设置实体的属性新值 + 将字符串转换为大写 + + BigApple -> BIGAPPLE - + - 实体对象 + 将字符串转换为小写 + + BigApple -> bigapple - - - 中断实体对象审计 - false: 每个实体对象的属性都会审计(默认) - true: 每个实体对象只审计一次 - - - - - ADO.NET 数据流读取对象 - - - - - DataReader 对应的 Index 位置 - - - - - DataReader 对应的 PropertyInfo - - - - - 获取 Index 对应的值,也可以设置拦截的新值 - - - - - 标识符,可将 CommandBefore 与 CommandAfter 进行匹配 - - - - - 状态数据,可与 CommandAfter 共享 - - - - - 发生的错误 - - - - - 执行SQL命令,返回的结果 - - - - - 耗时(单位:Ticks) - - - - - 耗时(单位:毫秒) - - - - - 标识符,可将 TraceBeforeEventArgs 与 TraceAfterEventArgs 进行匹配 - - - - - 状态数据,可与 TraceAfter 共享 - - - - - 备注 - - - - - 发生的错误 - - - - - 耗时(单位:Ticks) - - - - - 耗时(单位:毫秒) - - - - - 【开发环境必备】自动同步实体结构到数据库,程序运行中检查实体表是否存在,然后创建或修改 - - - - - 转小写同步结构,适用 PostgreSQL - - - - - 转大写同步结构,适用 Oracle/达梦/人大金仓 - - - - - 将数据库的主键、自增、索引设置导入,适用 DbFirst 模式,无须在实体类型上设置 [Column(IsPrimary)] 或者 ConfigEntity。此功能目前可用于 mysql/sqlserver/postgresql/oracle。 - 本功能会影响 IFreeSql 首次访问的速度。 - 若使用 CodeFirst 创建索引后,又直接在数据库上建了索引,若无本功能下一次 CodeFirst 迁移时数据库上创建的索引将被删除 - - - - - 不使用命令参数化执行,针对 Insert/Update - - - + 是否生成命令参数化执行,针对 lambda 表达式解析 注意:常量不会参数化,变量才会做参数化 @@ -4218,99 +4343,7 @@ 获取 FreeSql FluentApi 配置实体的元数据 - 未使用ConfigEntity配置时,返回null - - - - 获取实体类核心配置 - - - - - - - 获取所有数据库 - - - - - - 获取指定数据库的表信息,包括表、列详情、主键、唯一键、索引、外键、备注 - - - - - - - 获取指定单表信息,包括列详情、主键、唯一键、索引、备注 - - 表名,如:dbo.table1 - 是否忽略大小写 - - - - - 判断表是否存在 - - 表名,如:dbo.table1 - 是否忽略大小写 - - - - - 获取数据库枚举类型int值 - - - - - - - 获取c#转换,(int)、(long) - - - - - - - 获取c#值 - - - - - - - 获取c#类型,int、long - - - - - - - 获取c#类型对象 - - - - - - - 获取ado.net读取方法, GetBoolean、GetInt64 - - - - - - - 序列化 - - - - - - - 反序列化 - - - + 未使用ConfigEntity配置时, @@ -4404,6 +4437,93 @@ fsql.CodeFirst.ConfigEntity(a => a.Property(b => b.Id).Name("table_id")) + + + AOP 特性 https://github.com/dotnetcore/FreeSql/wiki/AOPF:FreeSql.Internal.BaseDiyMemberExpression._lambdaParameter"> + + 临时 LambdaExpression.Parameter + + + + + 如果实体类有自增属性,分成两个 List,有值的Item1 merge,无值的Item2 insert + + + + + + + AsType, Ctor, ClearData 三处地方需要重新加载 + + + + + AsType, Ctor, ClearData 三处地方需要重新加载 + + + + + 动态读取 DescriptionAttribute 注释文本 + + + + + + + 通过属性的注释文本,通过 xml 读取 + + + Dict:key=属性名,value=注释 + + + + 更新实体的元数据 + + + + + 执行更新的 SQL + + + + + 执行更新命令的参数 + + + + + 执行更新命令影响的行 + + + + + 更新的实体数量 + + + + + 更新的实体 + + + + + 映射优先级,默认: Attribute > FluentApi > Aop + + + + + 实体特性 + [Table(Name = "tabname")] + [Column(Name = "table_id")] + + + + + 流式接口 + fsql.CodeFirst.ConfigEntity(a => a.Name("tabname")) + fsql.CodeFirst.ConfigEntity(a => a.Property(b => b.Id).Name("table_id")) + + AOP 特性 https://github.com/dotnetcore/FreeSql/wiki/AOP @@ -5808,6 +5928,28 @@ 请使用 fsql.InsertDict(dict) 方法插入字典数据 + + + 动态构建Class Type + + + + + + 根据字典,创建 table 对应的实体对象 + + + + + + + + 根据实体对象,创建 table 对应的字典 + + + + + C#: that >= between && that <= and @@ -6324,115 +6466,3 @@ -`0})"> - - 插入数据,传入实体集合 - - - - - - - - 插入或更新数据,此功能依赖数据库特性(低版本可能不支持),参考如下: - 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 方法(不依赖数据库特性) - - - - - - - 修改数据 - - - - - - - 修改数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 查询数据 - - - - - - - 查询数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 删除数据 - - - - - - - 删除数据,传入动态条件,如:主键值 | new[]{主键值1,主键值2} | TEntity1 | new[]{TEntity1,TEntity2} | new{id=1} - - - 主键值、主键值集合、实体、实体集合、匿名对象、匿名对象集合 - - - - - 开启事务(不支持异步) - v1.5.0 关闭了线程事务超时自动提交的机制 - - 事务体 () => {} - - - - 开启事务(不支持异步) - v1.5.0 关闭了线程事务超时自动提交的机制 - - - 事务体 () => {} - - - - 数据库访问对象 - - - - - 所有拦截方法都在这里 - - - - - CodeFirst 模式开发相关方法 - - - - - DbFirst 模式开发相关方法 - - - - - 全局过滤设置,可默认附加为 Select/Update/Delete 条件 - - - - diff --git a/FreeSql/Internal/CommonProvider/DeleteProvider.cs b/FreeSql/Internal/CommonProvider/DeleteProvider.cs index bb22abf1..1beb2e84 100644 --- a/FreeSql/Internal/CommonProvider/DeleteProvider.cs +++ b/FreeSql/Internal/CommonProvider/DeleteProvider.cs @@ -212,7 +212,7 @@ namespace FreeSql.Internal.CommonProvider if (_table.AsTableImpl != null && string.IsNullOrWhiteSpace(_tableRule?.Invoke(_table.DbName)) == true) { var oldTableRule = _tableRule; - var names = _table.AsTableImpl.GetTableNamesBySqlWhere(newwhere.ToString(), _params, new SelectTableInfo { Table = _table }, _commonUtils); + var names = _table.AsTableImpl.GetTableNamesBySqlWhere(newwhere.ToString(), _params, new SelectTableInfo { Table = _table }, _commonUtils).Names; foreach (var name in names) { _tableRule = old => name; @@ -247,7 +247,7 @@ namespace FreeSql.Internal.CommonProvider if (_table.AsTableImpl != null && string.IsNullOrWhiteSpace(_tableRule?.Invoke(_table.DbName)) == true) { var oldTableRule = _tableRule; - var names = _table.AsTableImpl.GetTableNamesBySqlWhere(newwhere.ToString(), _params, new SelectTableInfo { Table = _table }, _commonUtils); + var names = _table.AsTableImpl.GetTableNamesBySqlWhere(newwhere.ToString(), _params, new SelectTableInfo { Table = _table }, _commonUtils).Names; foreach (var name in names) { _tableRule = old => name; diff --git a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs index 6b00724b..f814035f 100644 --- a/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs +++ b/FreeSql/Internal/CommonProvider/SelectProvider/Select0Provider.cs @@ -1,4 +1,5 @@ -using FreeSql.Internal.Model; +using FreeSql.DataAnnotations; +using FreeSql.Internal.Model; using System; using System.Collections; using System.Collections.Concurrent; @@ -49,7 +50,8 @@ namespace FreeSql.Internal.CommonProvider public BaseDiyMemberExpression _diymemexpWithTempQuery; public Func _resolveHookTransaction; - public bool IsDefaultSqlContent => _distinct == false && _is_AsTreeCte == false && _tables.Count == 1 && _where.Length == 0 && _join.Length == 0 && + public bool IsDefaultSqlContent => _tables.Count == 1 && _tables[0].Table?.AsTableImpl == null && + _distinct == false && _is_AsTreeCte == false && _where.Length == 0 && _join.Length == 0 && string.IsNullOrWhiteSpace(_orderby) && string.IsNullOrWhiteSpace(_groupby) && string.IsNullOrWhiteSpace(_tosqlAppendContent) && _aliasRule == null && _selectExpression == null; @@ -868,25 +870,36 @@ namespace FreeSql.Internal.CommonProvider var unions = new List>(); var trs = _tableRules.Any() ? _tableRules : new List>(new[] { new Func((type, oldname) => null) }); - if (trs.Count == 1 && _tables.Any(a => a.Table != null && a.Table.AsTableImpl != null && string.IsNullOrWhiteSpace(trs[0](a.Table.Type, a.Table.DbName)) == true)) + if (trs.Count == 1 && _tables.Any(a => a.Table != null && a.Table.AsTableImpl != null && + string.IsNullOrWhiteSpace(trs[0](a.Table.Type, a.Table.AsTableImpl != null ? null : a.Table.DbName)) == true)) { - string[] LocalGetTableNames(SelectTableInfo tb) + DateTime? DateTimeAsTableImplStart = null, DateTimeAsTableImplEnd = null; + string[] LocalGetTableNames(SelectTableInfo tb) { - var trname = trs[0](tb.Table.Type, tb.Table.DbName); + var trname = trs[0](tb.Table.Type, tb.Table.AsTableImpl != null ? null : tb.Table.DbName); if (tb.Table.AsTableImpl != null && string.IsNullOrWhiteSpace(trname) == true) { - string[] aret = null; - if (_where.Length == 0) aret = tb.Table.AsTableImpl.AllTables; - else aret = tb.Table.AsTableImpl.GetTableNamesBySqlWhere(_where.ToString(), _params, tb, _commonUtils); - if (aret.Any() == false) aret = tb.Table.AsTableImpl.AllTables.Take(1).ToArray(); - - for (var a = 0; a < aret.Length; a++) + var aret = tb.Table.AsTableImpl.GetTableNamesBySqlWhere(_where.Length == 0 ? null : _where.ToString(), _params, tb, _commonUtils); + if (aret.Names.Any() == false) { - if (_orm.CodeFirst.IsSyncStructureToLower) aret[a] = aret[a].ToLower(); - if (_orm.CodeFirst.IsSyncStructureToUpper) aret[a] = aret[a].ToUpper(); - if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(tb.Table.Type, aret[a]); + var now = DateTime.Now; + aret = new IAsTableTableNameRangeResult(tb.Table.AsTableImpl.AllTables.Take(1).ToArray(), now, now); } - return aret; + + for (var a = 0; a < aret.Names.Length; a++) + { + if (_orm.CodeFirst.IsSyncStructureToLower) aret.Names[a] = aret.Names[a].ToLower(); + if (_orm.CodeFirst.IsSyncStructureToUpper) aret.Names[a] = aret.Names[a].ToUpper(); + if (_orm.CodeFirst.IsAutoSyncStructure) _orm.CodeFirst.SyncStructure(tb.Table.Type, aret.Names[a]); + } + if (tb.Table.AsTableImpl is DateTimeAsTableImpl) + { + if (aret.ColumnValue1 is DateTime dt1) + if (DateTimeAsTableImplStart == null || dt1 > DateTimeAsTableImplStart) DateTimeAsTableImplStart = dt1; + if (aret.ColumnValue2 is DateTime dt2) + if (DateTimeAsTableImplEnd == null || dt2 < DateTimeAsTableImplEnd) DateTimeAsTableImplEnd = dt2; + } + return aret.Names; } if (string.IsNullOrWhiteSpace(trname) == false) { @@ -902,13 +915,17 @@ namespace FreeSql.Internal.CommonProvider } return new string[] { tb.Table.DbName }; } - var tbnames = _tables.GroupBy(a => a.Table.Type).Select(g => _tables.Where(a => a.Table.Type == g.Key).FirstOrDefault()).Select(a => new { Tb = a, Names = LocalGetTableNames(a) }).ToList(); - var dict = new Dictionary(); + var tbnames = _tables.Where(a => a.Type != SelectTableInfoType.Parent).GroupBy(a => a.Table.Type).Select(g => _tables.Where(a => a.Table.Type == g.Key).FirstOrDefault()).Select(a => new { Tb = a, Names = LocalGetTableNames(a) }).ToList(); + if (DateTimeAsTableImplStart != null && DateTimeAsTableImplEnd != null && tbnames.Where(a => a.Names.Length > 1).Count() > 1) + { + tbnames = tbnames.Select(a => new { a.Tb, Names = a.Tb.Table.AsTableImpl?.GetTableNamesByColumnValueRange(DateTimeAsTableImplStart, DateTimeAsTableImplEnd) ?? a.Names }).ToList(); + } + var dict = new Dictionary(); tbnames.ForEach(a => { dict.Add(a.Tb.Table.Type, a.Names[0]); }); - unions.Add(dict); + unions.Add(dict); for (var a = 0; a < tbnames.Count; a++) { if (tbnames[a].Names.Length <= 1) continue; @@ -920,10 +937,34 @@ namespace FreeSql.Internal.CommonProvider dict = new Dictionary(); foreach (var uit in unions[d]) dict.Add(uit.Key, uit.Key == tbnames[a].Tb.Table.Type ? tbnames[a].Names[b] : uit.Value); - unions.Add(dict); + unions.Add(dict); } } } + if (DateTimeAsTableImplStart != null && DateTimeAsTableImplEnd != null && tbnames.Where(a => a.Names.Length > 1).Count() > 1) + { + for (var uidx = unions.Count - 1; uidx >= 0; uidx--) + { + var ignore = false; + DateTime? dtStart = null, dtEnd = null; + foreach (var ut in unions[uidx]) + { + if (tbnames.Where(a => a.Tb.Table.Type == ut.Key).FirstOrDefault()?.Tb.Table?.AsTableImpl is DateTimeAsTableImpl dtImpl == false) continue; + var dtRange = dtImpl.GetRangeByTableName(ut.Value); + if (dtRange == null) continue; + if (dtStart == null) dtStart = dtRange.Item1; + if (dtEnd == null) dtEnd = dtRange.Item2; + if (dtRange.Item1 >= dtEnd || dtRange.Item2 <= dtStart) + { + ignore = true; + break; + } + if (dtRange.Item1 > dtStart) dtStart = dtRange.Item1; + if (dtRange.Item2 < dtEnd) dtEnd = dtRange.Item2; + } + if (ignore) unions.RemoveAt(uidx); + } + } return unions; } if (trs.Any() == false) trs.Add(new Func((type, oldname) => null)); diff --git a/FreeSql/Internal/CommonProvider/UpdateProvider.cs b/FreeSql/Internal/CommonProvider/UpdateProvider.cs index ddb9bc2d..3d39efe0 100644 --- a/FreeSql/Internal/CommonProvider/UpdateProvider.cs +++ b/FreeSql/Internal/CommonProvider/UpdateProvider.cs @@ -971,7 +971,7 @@ namespace FreeSql.Internal.CommonProvider if (_table.AsTableImpl != null && string.IsNullOrWhiteSpace(_tableRule?.Invoke(_table.DbName)) == true) { var oldTableRule = _tableRule; - var names = _table.AsTableImpl.GetTableNamesBySqlWhere(newwhere.ToString(), _params, new SelectTableInfo { Table = _table }, _commonUtils); + var names = _table.AsTableImpl.GetTableNamesBySqlWhere(newwhere.ToString(), _params, new SelectTableInfo { Table = _table }, _commonUtils).Names; foreach (var name in names) { _tableRule = old => name; @@ -1005,7 +1005,7 @@ namespace FreeSql.Internal.CommonProvider if (_table.AsTableImpl != null && string.IsNullOrWhiteSpace(_tableRule?.Invoke(_table.DbName)) == true) { var oldTableRule = _tableRule; - var names = _table.AsTableImpl.GetTableNamesBySqlWhere(newwhere.ToString(), _params, new SelectTableInfo { Table = _table }, _commonUtils); + var names = _table.AsTableImpl.GetTableNamesBySqlWhere(newwhere.ToString(), _params, new SelectTableInfo { Table = _table }, _commonUtils).Names; foreach (var name in names) { _tableRule = old => name;