diff --git a/Extensions/FreeSql.Extensions.AggregateRoot/AggregateRootRepository/AggregateRootUtils.cs b/Extensions/FreeSql.Extensions.AggregateRoot/AggregateRootRepository/AggregateRootUtils.cs index 1f475509..f2cf5fc3 100644 --- a/Extensions/FreeSql.Extensions.AggregateRoot/AggregateRootRepository/AggregateRootUtils.cs +++ b/Extensions/FreeSql.Extensions.AggregateRoot/AggregateRootRepository/AggregateRootUtils.cs @@ -135,7 +135,7 @@ namespace FreeSql } if (collectionBefore != null && collectionAfter == null) { - //foreach (var item in collectionBefore as IEnumerable) + //foreach (var item in collectionBefore) //{ // changelog.DeleteLog.Add(NativeTuple.Create(elementType, new[] { item })); // NavigateReader(boundaryName, fsql, elementType, item, (path, tr, ct, stackvs) => @@ -148,12 +148,12 @@ namespace FreeSql } Dictionary dictBefore = new Dictionary(); Dictionary dictAfter = new Dictionary(); - foreach (var item in collectionBefore as IEnumerable) + foreach (var item in collectionBefore) { var key = fsql.GetEntityKeyString(elementType, item, false); if (key != null) dictBefore.Add(key, item); } - foreach (var item in collectionAfter as IEnumerable) + foreach (var item in collectionAfter) { var key = fsql.GetEntityKeyString(elementType, item, false); if (key != null) @@ -385,7 +385,7 @@ namespace FreeSql if (propvalFrom == null) { EntityUtilExtensions.SetEntityValueWithPropertyName(fsql, entityType, entityTo, prop.Name, null); - return; + continue; } switch (tbref.RefType) { diff --git a/FreeSql.DbContext/FreeSql.DbContext.xml b/FreeSql.DbContext/FreeSql.DbContext.xml index 26522f10..537315e2 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.xml +++ b/FreeSql.DbContext/FreeSql.DbContext.xml @@ -800,14 +800,5 @@ - - - 批量注入 Repository,可以参考代码自行调整 - - - - - - diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj index 6e4097e1..1eb8fe0b 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -22,7 +22,7 @@ - + diff --git a/FreeSql/FreeSql.xml b/FreeSql/FreeSql.xml index 3a04ca77..f4a66a58 100644 --- a/FreeSql/FreeSql.xml +++ b/FreeSql/FreeSql.xml @@ -1086,6 +1086,82 @@ + + + 动态创建实体类型 + + + + + 配置Class + + 类名 + 类标记的特性[Table(Name = "xxx")] [Index(xxxx)] + + + + + 配置属性 + + 属性名称 + 属性类型 + 属性标记的特性-支持多个 + + + + + 配置属性 + + 属性名称 + 属性类型 + 该属性是否重写父类属性 + 属性标记的特性-支持多个 + + + + + 配置属性 + + 属性名称 + 属性类型 + 该属性是否重写父类属性 + 属性默认值 + 属性标记的特性-支持多个 + + + + + 配置父类 + + 父类类型 + + + + + Override属性 + + + + + + Emit动态创建出Class - Type + + + + + + 首字母小写 + + + + + + + 首字母大写 + + + + 获取实体的主键值,以 "*|_,[,_|*" 分割,当任意一个主键属性无值时,返回 null @@ -5769,6 +5845,28 @@ 请使用 fsql.InsertDict(dict) 方法插入字典数据 + + + 动态构建Class Type + + + + + + 根据字典,创建 table 对应的实体对象 + + + + + + + + 根据实体对象,创建 table 对应的字典 + + + + + C#: that >= between && that <= and @@ -6285,115 +6383,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/Model/TableInfo.cs b/FreeSql/Internal/Model/TableInfo.cs index f06fe7d2..ac9ed301 100644 --- a/FreeSql/Internal/Model/TableInfo.cs +++ b/FreeSql/Internal/Model/TableInfo.cs @@ -26,7 +26,7 @@ namespace FreeSql.Internal.Model public bool DisableSyncStructure { get; set; } public string Comment { get; set; } public bool IsRereadSql { get; internal set; } - public bool IsDictionaryType { get; internal set; } + public bool IsDictionaryType { get; set; } public IAsTable AsTableImpl { get; internal set; } public ColumnInfo AsTableColumn { get; internal set; } diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index d3f2caff..4e4ec68d 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -112,477 +112,22 @@ namespace FreeSql.Internal continue; } if (tp == null && colattr != null) colattr.IsIgnore = true; //无法匹配的属性,认定是导航属性,且自动过滤 - var colattrIsNullable = colattr?._IsNullable; - var colattrIsNull = colattr == null; - if (colattr == null) - colattr = new ColumnAttribute - { - Name = p.Name, - DbType = tp.dbtypeFull, - IsNullable = tp.isnullable ?? true, - MapType = p.PropertyType - }; - if (colattr._IsNullable == null) colattr._IsNullable = tp?.isnullable; - if (string.IsNullOrEmpty(colattr.DbType)) colattr.DbType = tp?.dbtypeFull ?? "varchar(255)"; - if (colattr.DbType.StartsWith("set(") || colattr.DbType.StartsWith("enum(")) - { - var leftBt = colattr.DbType.IndexOf('('); - colattr.DbType = colattr.DbType.Substring(0, leftBt).ToUpper() + colattr.DbType.Substring(leftBt); - } - else if (common._orm.Ado.DataType != DataType.ClickHouse) - colattr.DbType = colattr.DbType.ToUpper(); - if (colattrIsNull == false && colattrIsNullable == true) colattr.DbType = colattr.DbType.Replace("NOT NULL", ""); - if (colattrIsNull == false && colattrIsNullable == false && colattr.DbType.Contains("NOT NULL") == false) colattr.DbType = Regex.Replace(colattr.DbType, @"\bNULL\b", "").Trim() + " NOT NULL"; - if (colattr._IsNullable == null && tp != null && tp.isnullable == null) colattr.IsNullable = tp.dbtypeFull.Contains("NOT NULL") == false; - if (colattr.DbType?.Contains("NOT NULL") == true) colattr.IsNullable = false; - if (string.IsNullOrEmpty(colattr.Name)) colattr.Name = p.Name; - if (common.CodeFirst.IsSyncStructureToLower) colattr.Name = colattr.Name.ToLower(); - if (common.CodeFirst.IsSyncStructureToUpper) colattr.Name = colattr.Name.ToUpper(); + var col = ColumnAttributeToInfo(trytb, entityDefault, p.Name, p.PropertyType, setMethod == null, ref colattr, tp, common); + if (col == null) continue; - if ((colattr.IsNullable != true || colattr.IsIdentity == true || colattr.IsPrimary == true) && colattr.DbType.Contains("NOT NULL") == false && common._orm.Ado.DataType != DataType.ClickHouse) - { - colattr.IsNullable = false; - colattr.DbType = Regex.Replace(colattr.DbType, @"\bNULL\b", "").Trim() + " NOT NULL"; - } - if (colattr.IsNullable == true && colattr.DbType.Contains("NOT NULL")) colattr.DbType = colattr.DbType.Replace("NOT NULL", ""); - else if (colattr.IsNullable == true && !colattr.DbType.Contains("Nullable") && common._orm.Ado.DataType == DataType.ClickHouse) colattr.DbType = $"Nullable({colattr.DbType})"; - colattr.DbType = Regex.Replace(colattr.DbType, @"\([^\)]+\)", m => - { - var tmpLt = Regex.Replace(m.Groups[0].Value, @"\s", ""); - if (tmpLt.Contains("CHAR")) tmpLt = tmpLt.Replace("CHAR", " CHAR"); - if (tmpLt.Contains("BYTE")) tmpLt = tmpLt.Replace("BYTE", " BYTE"); - return tmpLt; - }); - if (colattr.IsIdentity == true && colattr.MapType.IsNumberType() == false) - colattr.IsIdentity = false; - if (setMethod == null) colattr.IsIgnore = true; + if (propsComment != null && propsComment.TryGetValue(p.Name, out var trycomment)) + col.Comment = trycomment; + if (string.IsNullOrEmpty(col.Comment) && propsCommentByDescAttr != null && propsCommentByDescAttr.TryGetValue(p.Name, out trycomment)) + col.Comment = trycomment; - var col = new ColumnInfo - { - Table = trytb, - CsName = p.Name, - CsType = p.PropertyType, - Attribute = colattr - }; - if (propsComment != null && propsComment.TryGetValue(p.Name, out var trycomment)) - col.Comment = trycomment; - if (string.IsNullOrEmpty(col.Comment) && propsCommentByDescAttr != null && propsCommentByDescAttr.TryGetValue(p.Name, out trycomment)) - col.Comment = trycomment; - - if (colattr.IsIgnore) - { - trytb.ColumnsByCsIgnore.Add(p.Name, col); - continue; - } - object defaultValue = null; - if (entityDefault != null) defaultValue = trytb.Properties[p.Name].GetValue(entityDefault, null); - if (p.PropertyType.IsEnum) - { - var isEqualsEnumValue = false; - var enumValues = Enum.GetValues(p.PropertyType); - for (var a = 0; a < enumValues.Length; a++) - if (object.Equals(defaultValue, enumValues.GetValue(a))) - { - isEqualsEnumValue = true; - break; - } - if (isEqualsEnumValue == false && enumValues.Length > 0) - defaultValue = enumValues.GetValue(0); - } - if (defaultValue != null && p.PropertyType != colattr.MapType) defaultValue = Utils.GetDataReaderValue(colattr.MapType, defaultValue); - if (defaultValue == null) defaultValue = tp?.defaultValue; - if (colattr.IsNullable == false && defaultValue == null) - { - var citype = colattr.MapType.IsNullableType() ? colattr.MapType.GetGenericArguments().FirstOrDefault() : colattr.MapType; - defaultValue = citype.CreateInstanceGetDefaultValue(); - } - try - { - var initParms = new List(); - col.DbDefaultValue = common.GetNoneParamaterSqlValue(initParms, "init", col, colattr.MapType, defaultValue); - if (initParms.Any()) col.DbDefaultValue = "NULL"; - } - catch - { - col.DbDefaultValue = "NULL"; - } - //if (defaultValue != null && colattr.MapType.NullableTypeOrThis() == typeof(DateTime)) - //{ - // var dt = (DateTime)defaultValue; - // if (Math.Abs(dt.Subtract(DateTime.Now).TotalSeconds) < 60) - // col.DbDefaultValue = common.Now; - // else if (Math.Abs(dt.Subtract(DateTime.UtcNow).TotalSeconds) < 60) - // col.DbDefaultValue = common.NowUtc; - //} - - if (common._orm.Ado.DataType == DataType.GBase) - { - if (colattr.IsIdentity == true) - { - var colType = col.CsType.NullableTypeOrThis(); - if (colType == typeof(int) || colType == typeof(uint)) - colattr.DbType = "SERIAL"; - else if (colType == typeof(long) || colType == typeof(ulong)) - colattr.DbType = "SERIAL8"; - } - if (colattr.MapType.NullableTypeOrThis() == typeof(DateTime)) - { - if (colattr._Precision == null) - { - colattr.DbType = "DATETIME YEAR TO FRACTION(3)"; - colattr.Precision = 3; - col.DbPrecision = 3; - } - else if (colattr._Precision == 0) - { - colattr.DbType = "DATETIME YEAR TO SECOND"; - } - else if (colattr._Precision > 0) - { - colattr.DbType = $"DATETIME YEAR TO FRACTION({colattr.Precision})"; - col.DbPrecision = (byte)colattr.Precision; - } - } - } - if (colattr.ServerTime != DateTimeKind.Unspecified && new[] { typeof(DateTime), typeof(DateTimeOffset) }.Contains(colattr.MapType.NullableTypeOrThis())) - { - var commonNow = common.Now; - var commonNowUtc = common.NowUtc; - switch (common._orm.Ado.DataType) - { - case DataType.MySql: - case DataType.OdbcMySql: //处理毫秒 - case DataType.CustomMySql: - var timeLength = 0; - var mTimeLength = Regex.Match(colattr.DbType, @"(DATETIME|TIMESTAMP)\s*\((\d+)\)"); - if (mTimeLength.Success) timeLength = int.Parse(mTimeLength.Groups[2].Value); - if (timeLength > 0 && timeLength < 7) - { - commonNow = $"{commonNow.TrimEnd('(', ')')}({timeLength})"; - commonNowUtc = $"{commonNowUtc.TrimEnd('(', ')')}({timeLength})"; - } - //https://github.com/dotnetcore/FreeSql/issues/1604 mysql 不支持默认值 utc_timestamp DDL - if (colattr.ServerTime == DateTimeKind.Local) - col.DbDefaultValue = commonNow; - break; - default: - col.DbDefaultValue = colattr.ServerTime == DateTimeKind.Local ? commonNow : commonNowUtc; - break; - } - col.DbInsertValue = colattr.ServerTime == DateTimeKind.Local ? commonNow : commonNowUtc; - col.DbUpdateValue = colattr.ServerTime == DateTimeKind.Local ? commonNow : commonNowUtc; - } - if (string.IsNullOrEmpty(colattr.InsertValueSql) == false) - { - col.DbDefaultValue = colattr.InsertValueSql; - col.DbInsertValue = colattr.InsertValueSql; - } - if (colattr.MapType.NullableTypeOrThis() == typeof(string) && colattr.StringLength != 0) - { - int strlen = colattr.StringLength; - var charPatten = @"(CHARACTER|CHAR2|CHAR)\s*(\([^\)]*\))?"; - var strNotNull = colattr.IsNullable == false ? " NOT NULL" : ""; - switch (common._orm.Ado.DataType) - { - case DataType.MySql: - case DataType.OdbcMySql: - case DataType.CustomMySql: - if (strlen == -2) colattr.DbType = $"LONGTEXT{strNotNull}"; - else if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; - else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); - break; - case DataType.SqlServer: - case DataType.OdbcSqlServer: - case DataType.CustomSqlServer: - if (strlen < 0) colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1(MAX)"); - else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); - break; - case DataType.PostgreSQL: - case DataType.OdbcPostgreSQL: - case DataType.CustomPostgreSQL: - case DataType.KingbaseES: - case DataType.OdbcKingbaseES: - case DataType.ShenTong: - if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; - else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); - break; - case DataType.Oracle: - case DataType.CustomOracle: - if (strlen < 0) colattr.DbType = $"NCLOB{strNotNull}"; //v1.3.2+ https://github.com/dotnetcore/FreeSql/issues/259 - else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); - break; - case DataType.Dameng: - if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; - else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); - break; - case DataType.OdbcOracle: - case DataType.OdbcDameng: - if (strlen < 0) colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1(4000)"); //ODBC 不支持 NCLOB - else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); - break; - case DataType.Sqlite: - if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; - else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); - break; - case DataType.MsAccess: - charPatten = @"(CHAR|CHAR2|CHARACTER|TEXT)\s*(\([^\)]*\))?"; - if (strlen < 0) colattr.DbType = $"LONGTEXT{strNotNull}"; - else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); - break; - case DataType.Firebird: - charPatten = @"(CHAR|CHAR2|CHARACTER|TEXT)\s*(\([^\)]*\))?"; - if (strlen < 0) colattr.DbType = $"BLOB SUB_TYPE 1{strNotNull}"; - else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); - break; - case DataType.GBase: - if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; - else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); - break; - } - } - if (colattr.MapType == typeof(string) && colattr.IsVersion == true) colattr.StringLength = 40; - if (colattr.MapType == typeof(byte[]) && colattr.IsVersion == true) colattr.StringLength = 16; // 8=sqlserver timestamp, 16=GuidToBytes - if (colattr.MapType == typeof(byte[]) && colattr.StringLength != 0) - { - int strlen = colattr.StringLength; - var bytePatten = @"(VARBINARY|BINARY|BYTEA)\s*(\([^\)]*\))?"; - var strNotNull = colattr.IsNullable == false ? " NOT NULL" : ""; - switch (common._orm.Ado.DataType) - { - case DataType.MySql: - case DataType.OdbcMySql: - case DataType.CustomMySql: - if (strlen == -2) colattr.DbType = $"LONGBLOB{strNotNull}"; - else if (strlen < 0) colattr.DbType = $"BLOB{strNotNull}"; - else colattr.DbType = Regex.Replace(colattr.DbType, bytePatten, $"$1({strlen})"); - break; - case DataType.SqlServer: - case DataType.OdbcSqlServer: - case DataType.CustomSqlServer: - if (strlen < 0) colattr.DbType = Regex.Replace(colattr.DbType, bytePatten, $"$1(MAX)"); - else colattr.DbType = Regex.Replace(colattr.DbType, bytePatten, $"$1({strlen})"); - break; - case DataType.PostgreSQL: - case DataType.OdbcPostgreSQL: - case DataType.CustomPostgreSQL: - case DataType.KingbaseES: - case DataType.OdbcKingbaseES: - case DataType.ShenTong: //驱动引发的异常:“System.Data.OscarClient.OscarException”(位于 System.Data.OscarClient.dll 中) - colattr.DbType = $"BYTEA{strNotNull}"; //变长二进制串 - break; - case DataType.Oracle: - case DataType.CustomOracle: - colattr.DbType = $"BLOB{strNotNull}"; - break; - case DataType.Dameng: - colattr.DbType = $"BLOB{strNotNull}"; - break; - case DataType.OdbcOracle: - case DataType.OdbcDameng: - colattr.DbType = $"BLOB{strNotNull}"; - break; - case DataType.Sqlite: - colattr.DbType = $"BLOB{strNotNull}"; - break; - case DataType.MsAccess: - if (strlen < 0) colattr.DbType = $"BLOB{strNotNull}"; - else colattr.DbType = Regex.Replace(colattr.DbType, bytePatten, $"$1({strlen})"); - break; - case DataType.Firebird: - colattr.DbType = $"BLOB{strNotNull}"; - break; - case DataType.GBase: - colattr.DbType = $"BYTE{strNotNull}"; - break; - } - } - if (colattr.MapType.NullableTypeOrThis() == typeof(decimal) && (colattr.Precision > 0 || colattr.Scale > 0)) - { - if (colattr.Precision <= 0) colattr.Precision = 10; - if (colattr.Scale <= 0) colattr.Scale = 0; - var decimalPatten = @"(DECIMAL|NUMERIC|NUMBER)\s*(\([^\)]*\))?"; - colattr.DbType = Regex.Replace(colattr.DbType, decimalPatten, $"$1({colattr.Precision},{colattr.Scale})"); - } - - if (trytb.Columns.ContainsKey(colattr.Name)) throw new Exception(CoreStrings.Duplicate_ColumnAttribute(colattr.Name)); - if (trytb.ColumnsByCs.ContainsKey(p.Name)) throw new Exception(CoreStrings.Duplicate_PropertyName(p.Name)); - - trytb.Columns.Add(colattr.Name, col); + trytb.Columns.Add(colattr.Name, col); trytb.ColumnsByCs.Add(p.Name, col); columnsList.Add(col); } - trytb.VersionColumn = trytb.Columns.Values.Where(a => a.Attribute.IsVersion == true).LastOrDefault(); - if (trytb.VersionColumn != null) - { - if (trytb.VersionColumn.Attribute.MapType.IsNullableType() || - trytb.VersionColumn.Attribute.MapType.IsNumberType() == false && !new[] { typeof(byte[]), typeof(string) }.Contains(trytb.VersionColumn.Attribute.MapType)) - throw new Exception(CoreStrings.Properties_AsRowLock_Must_Numeric_Byte(trytb.VersionColumn.CsName)); - } - tbattr?.ParseAsTable(trytb); - var indexesDict = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - //从数据库查找主键、自增、索引 - if (common.CodeFirst.IsConfigEntityFromDbFirst) - { - try - { - if (common._orm.DbFirst != null) - { - if (common.dbTables == null) - lock (common.dbTablesLock) - if (common.dbTables == null) - common.dbTables = common._orm.DbFirst.GetTablesByDatabase(); - - var finddbtbs = common.dbTables.Where(a => string.Compare(a.Name, trytb.CsName, true) == 0 || string.Compare(a.Name, trytb.DbName, true) == 0); - foreach (var dbtb in finddbtbs) - { - foreach (var dbident in dbtb.Identitys) - { - if (trytb.Columns.TryGetValue(dbident.Name, out var trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbident.CsType.NullableTypeOrThis() || - trytb.ColumnsByCs.TryGetValue(dbident.Name, out trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbident.CsType.NullableTypeOrThis()) - trycol.Attribute.IsIdentity = true; - } - foreach (var dbpk in dbtb.Primarys) - { - if (trytb.Columns.TryGetValue(dbpk.Name, out var trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbpk.CsType.NullableTypeOrThis() || - trytb.ColumnsByCs.TryGetValue(dbpk.Name, out trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbpk.CsType.NullableTypeOrThis()) - trycol.Attribute.IsPrimary = true; - } - foreach (var dbidx in dbtb.IndexesDict) - { - var indexColumns = new List(); - foreach (var dbcol in dbidx.Value.Columns) - { - if (trytb.Columns.TryGetValue(dbcol.Column.Name, out var trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbcol.Column.CsType.NullableTypeOrThis() || - trytb.ColumnsByCs.TryGetValue(dbcol.Column.Name, out trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbcol.Column.CsType.NullableTypeOrThis()) - indexColumns.Add(new IndexColumnInfo - { - Column = trycol, - IsDesc = dbcol.IsDesc - }); - } - if (indexColumns.Any() == false) continue; - if (indexesDict.ContainsKey(dbidx.Key)) indexesDict.Remove(dbidx.Key); - indexesDict.Add(dbidx.Key, new IndexInfo - { - Name = dbidx.Key, - Columns = indexColumns.ToArray(), - IsUnique = dbidx.Value.IsUnique, - IndexMethod = IndexMethod.B_Tree - }); - } - } - } - } - catch { } - } - //索引和唯一键 var indexes = common.GetEntityIndexAttribute(trytb.Type); - foreach (var index in indexes) - { - var val = index.Fields?.Trim(' ', '\t', ','); - if (string.IsNullOrEmpty(val)) continue; - var arr = val.Split(',').Select(a => a.Trim(' ', '\t').Trim()).Where(a => !string.IsNullOrEmpty(a)).ToArray(); - if (arr.Any() == false) continue; - var indexColumns = new List(); - foreach (var field in arr) - { - var idxcol = new IndexColumnInfo(); - if (field.EndsWith(" DESC", StringComparison.CurrentCultureIgnoreCase)) idxcol.IsDesc = true; - var colname = Regex.Replace(field, " (DESC|ASC)", "", RegexOptions.IgnoreCase); - if (trytb.ColumnsByCs.TryGetValue(colname, out var trycol) || trytb.Columns.TryGetValue(colname, out trycol)) - { - idxcol.Column = trycol; - indexColumns.Add(idxcol); - } - } - if (indexColumns.Any() == false) continue; - var indexName = common.CodeFirst.IsSyncStructureToLower ? index.Name.ToLower() : (common.CodeFirst.IsSyncStructureToUpper ? index.Name.ToUpper() : index.Name); - if (indexesDict.ContainsKey(indexName)) indexesDict.Remove(indexName); - indexesDict.Add(indexName, new IndexInfo - { - Name = indexName, - Columns = indexColumns.ToArray(), - IsUnique = index.IsUnique, - IndexMethod = index.IndexMethod - }); - } - trytb.Indexes = indexesDict.Values.ToArray(); - - trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsPrimary == true).ToArray(); - if (trytb.Primarys.Any() == false) - { - trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute._IsPrimary == null && string.Compare(a.Attribute.Name, "id", true) == 0).ToArray(); - if (trytb.Primarys.Any() == false) - { - var identcol = trytb.Columns.Values.Where(a => a.Attribute.IsIdentity == true).FirstOrDefault(); - if (identcol != null) trytb.Primarys = new[] { identcol }; - if (trytb.Primarys.Any() == false) - { - trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute._IsPrimary == null && string.Compare(a.Attribute.Name, $"{trytb.DbName}id", true) == 0).ToArray(); - if (trytb.Primarys.Any() == false) - { - trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute._IsPrimary == null && string.Compare(a.Attribute.Name, $"{trytb.DbName}_id", true) == 0).ToArray(); - } - } - } - foreach (var col in trytb.Primarys) - col.Attribute.IsPrimary = true; - } - foreach (var col in trytb.Primarys) - { - col.Attribute.IsNullable = false; - col.Attribute.DbType = col.Attribute.DbType.Replace("NOT NULL", "").Replace(" NULL", "").Trim(); - switch (common._orm.Ado.DataType) - { - case DataType.Sqlite: - col.Attribute.DbType += " NOT NULL"; //sqlite 主键也可以插入 null - break; - } - } - foreach (var col in trytb.Columns.Values) - { - if (col.Attribute.IsPrimary == false && col.Attribute.IsIdentity) col.Attribute.CanUpdate = false; - var ltp = @"\(([^\)]+)\)"; - col.DbTypeText = Regex.Replace(col.Attribute.DbType.Replace("NOT NULL", "").Replace(" NULL", "").Trim(), ltp, ""); - var m = Regex.Match(col.Attribute.DbType, ltp); - if (m.Success == false) continue; - var sizeStr = m.Groups[1].Value.Trim(); - if (sizeStr.EndsWith(" BYTE") || sizeStr.EndsWith(" CHAR")) sizeStr = sizeStr.Remove(sizeStr.Length - 5); //ORACLE - if (string.Compare(sizeStr, "max", true) == 0) - { - col.DbSize = -1; - continue; - } - var sizeArr = sizeStr.Split(','); - if (int.TryParse(sizeArr[0].Trim(), out var size) == false) continue; - if (col.Attribute.MapType.NullableTypeOrThis() == typeof(DateTime)) - { - col.DbScale = (byte)size; - if (col.Attribute.Scale <= 0) col.Attribute.Scale = col.DbScale; - continue; - } - if (sizeArr.Length == 1) - { - col.DbSize = size; - if (col.Attribute.StringLength <= 0) col.Attribute.StringLength = col.DbSize; - continue; - } - if (byte.TryParse(sizeArr[1], out var scale) == false) continue; - col.DbPrecision = (byte)size; - col.DbScale = scale; - if (col.Attribute.Precision <= 0) - { - col.Attribute.Precision = col.DbPrecision; - col.Attribute.Scale = col.DbScale; - } - } - trytb.IsRereadSql = trytb.Columns.Where(a => string.IsNullOrWhiteSpace(a.Value.Attribute.RereadSql) == false).Any(); - trytb.ColumnsByPosition = columnsList.Where(a => a.Attribute.Position > 0).OrderBy(a => a.Attribute.Position) - .Concat(columnsList.Where(a => a.Attribute.Position == 0)) - .Concat(columnsList.Where(a => a.Attribute.Position < 0).OrderBy(a => a.Attribute.Position)).ToArray(); - trytb.ColumnsByCanUpdateDbUpdateValue = columnsList.Where(a => a.Attribute.CanUpdate == true && string.IsNullOrEmpty(a.DbUpdateValue) == false).ToArray(); + AuditTableInfo(trytb, tbattr, indexes, columnsList, common); tbc.AddOrUpdate(entity, trytb, (oldkey, oldval) => trytb); #region 查找导航属性的关系、virtual 属性延时加载,动态产生新的重写类 @@ -635,6 +180,475 @@ namespace FreeSql.Internal return tbc.TryGetValue(entity, out var trytb2) ? trytb2 : trytb; } + public static ColumnInfo ColumnAttributeToInfo(TableInfo trytb, object entityDefault, string csName, Type mapType, bool isIgnore, ref ColumnAttribute colattr, DbInfoResult tp, CommonUtils common) + { + var colattrIsNullable = colattr?._IsNullable; + var colattrIsNull = colattr == null; + if (colattr == null) + colattr = new ColumnAttribute + { + Name = csName, + DbType = tp.dbtypeFull, + IsNullable = tp.isnullable ?? true, + MapType = mapType + }; + if (colattr._IsNullable == null) colattr._IsNullable = tp?.isnullable; + if (string.IsNullOrEmpty(colattr.DbType)) colattr.DbType = tp?.dbtypeFull ?? "varchar(255)"; + if (colattr.DbType.StartsWith("set(") || colattr.DbType.StartsWith("enum(")) + { + var leftBt = colattr.DbType.IndexOf('('); + colattr.DbType = colattr.DbType.Substring(0, leftBt).ToUpper() + colattr.DbType.Substring(leftBt); + } + else if (common._orm.Ado.DataType != DataType.ClickHouse) + colattr.DbType = colattr.DbType.ToUpper(); + + if (colattrIsNull == false && colattrIsNullable == true) colattr.DbType = colattr.DbType.Replace("NOT NULL", ""); + if (colattrIsNull == false && colattrIsNullable == false && colattr.DbType.Contains("NOT NULL") == false) colattr.DbType = Regex.Replace(colattr.DbType, @"\bNULL\b", "").Trim() + " NOT NULL"; + if (colattr._IsNullable == null && tp != null && tp.isnullable == null) colattr.IsNullable = tp.dbtypeFull.Contains("NOT NULL") == false; + if (colattr.DbType?.Contains("NOT NULL") == true) colattr.IsNullable = false; + if (string.IsNullOrEmpty(colattr.Name)) colattr.Name = csName; + if (common.CodeFirst.IsSyncStructureToLower) colattr.Name = colattr.Name.ToLower(); + if (common.CodeFirst.IsSyncStructureToUpper) colattr.Name = colattr.Name.ToUpper(); + + if ((colattr.IsNullable != true || colattr.IsIdentity == true || colattr.IsPrimary == true) && colattr.DbType.Contains("NOT NULL") == false && common._orm.Ado.DataType != DataType.ClickHouse) + { + colattr.IsNullable = false; + colattr.DbType = Regex.Replace(colattr.DbType, @"\bNULL\b", "").Trim() + " NOT NULL"; + } + if (colattr.IsNullable == true && colattr.DbType.Contains("NOT NULL")) colattr.DbType = colattr.DbType.Replace("NOT NULL", ""); + else if (colattr.IsNullable == true && !colattr.DbType.Contains("Nullable") && common._orm.Ado.DataType == DataType.ClickHouse) colattr.DbType = $"Nullable({colattr.DbType})"; + colattr.DbType = Regex.Replace(colattr.DbType, @"\([^\)]+\)", m => + { + var tmpLt = Regex.Replace(m.Groups[0].Value, @"\s", ""); + if (tmpLt.Contains("CHAR")) tmpLt = tmpLt.Replace("CHAR", " CHAR"); + if (tmpLt.Contains("BYTE")) tmpLt = tmpLt.Replace("BYTE", " BYTE"); + return tmpLt; + }); + if (colattr.IsIdentity == true && colattr.MapType.IsNumberType() == false) + colattr.IsIdentity = false; + if (isIgnore) colattr.IsIgnore = true; + + var col = new ColumnInfo + { + Table = trytb, + CsName = csName, + CsType = mapType, + Attribute = colattr + }; + + if (colattr.IsIgnore) + { + trytb.ColumnsByCsIgnore.Add(csName, col); + return null; + } + object defaultValue = null; + if (entityDefault != null) defaultValue = trytb.Properties[csName].GetValue(entityDefault, null); + if (defaultValue != null && mapType.IsEnum) + { + var isEqualsEnumValue = false; + var enumValues = Enum.GetValues(mapType); + for (var a = 0; a < enumValues.Length; a++) + if (object.Equals(defaultValue, enumValues.GetValue(a))) + { + isEqualsEnumValue = true; + break; + } + if (isEqualsEnumValue == false && enumValues.Length > 0) + defaultValue = enumValues.GetValue(0); + } + if (defaultValue != null && mapType != colattr.MapType) defaultValue = Utils.GetDataReaderValue(colattr.MapType, defaultValue); + if (defaultValue == null) defaultValue = tp?.defaultValue; + if (colattr.IsNullable == false && defaultValue == null) + { + var citype = colattr.MapType.IsNullableType() ? colattr.MapType.GetGenericArguments().FirstOrDefault() : colattr.MapType; + defaultValue = citype.CreateInstanceGetDefaultValue(); + } + try + { + var initParms = new List(); + col.DbDefaultValue = common.GetNoneParamaterSqlValue(initParms, "init", col, colattr.MapType, defaultValue); + if (initParms.Any()) col.DbDefaultValue = "NULL"; + } + catch + { + col.DbDefaultValue = "NULL"; + } + //if (defaultValue != null && colattr.MapType.NullableTypeOrThis() == typeof(DateTime)) + //{ + // var dt = (DateTime)defaultValue; + // if (Math.Abs(dt.Subtract(DateTime.Now).TotalSeconds) < 60) + // col.DbDefaultValue = common.Now; + // else if (Math.Abs(dt.Subtract(DateTime.UtcNow).TotalSeconds) < 60) + // col.DbDefaultValue = common.NowUtc; + //} + + if (common._orm.Ado.DataType == DataType.GBase) + { + if (colattr.IsIdentity == true) + { + var colType = col.CsType.NullableTypeOrThis(); + if (colType == typeof(int) || colType == typeof(uint)) + colattr.DbType = "SERIAL"; + else if (colType == typeof(long) || colType == typeof(ulong)) + colattr.DbType = "SERIAL8"; + } + if (colattr.MapType.NullableTypeOrThis() == typeof(DateTime)) + { + if (colattr._Precision == null) + { + colattr.DbType = "DATETIME YEAR TO FRACTION(3)"; + colattr.Precision = 3; + col.DbPrecision = 3; + } + else if (colattr._Precision == 0) + { + colattr.DbType = "DATETIME YEAR TO SECOND"; + } + else if (colattr._Precision > 0) + { + colattr.DbType = $"DATETIME YEAR TO FRACTION({colattr.Precision})"; + col.DbPrecision = (byte)colattr.Precision; + } + } + } + if (colattr.ServerTime != DateTimeKind.Unspecified && new[] { typeof(DateTime), typeof(DateTimeOffset) }.Contains(colattr.MapType.NullableTypeOrThis())) + { + var commonNow = common.Now; + var commonNowUtc = common.NowUtc; + switch (common._orm.Ado.DataType) + { + case DataType.MySql: + case DataType.OdbcMySql: //处理毫秒 + case DataType.CustomMySql: + var timeLength = 0; + var mTimeLength = Regex.Match(colattr.DbType, @"(DATETIME|TIMESTAMP)\s*\((\d+)\)"); + if (mTimeLength.Success) timeLength = int.Parse(mTimeLength.Groups[2].Value); + if (timeLength > 0 && timeLength < 7) + { + commonNow = $"{commonNow.TrimEnd('(', ')')}({timeLength})"; + commonNowUtc = $"{commonNowUtc.TrimEnd('(', ')')}({timeLength})"; + } + //https://github.com/dotnetcore/FreeSql/issues/1604 mysql 不支持默认值 utc_timestamp DDL + if (colattr.ServerTime == DateTimeKind.Local) + col.DbDefaultValue = commonNow; + break; + default: + col.DbDefaultValue = colattr.ServerTime == DateTimeKind.Local ? commonNow : commonNowUtc; + break; + } + col.DbInsertValue = colattr.ServerTime == DateTimeKind.Local ? commonNow : commonNowUtc; + col.DbUpdateValue = colattr.ServerTime == DateTimeKind.Local ? commonNow : commonNowUtc; + } + if (string.IsNullOrEmpty(colattr.InsertValueSql) == false) + { + col.DbDefaultValue = colattr.InsertValueSql; + col.DbInsertValue = colattr.InsertValueSql; + } + if (colattr.MapType.NullableTypeOrThis() == typeof(string) && colattr.StringLength != 0) + { + int strlen = colattr.StringLength; + var charPatten = @"(CHARACTER|CHAR2|CHAR)\s*(\([^\)]*\))?"; + var strNotNull = colattr.IsNullable == false ? " NOT NULL" : ""; + switch (common._orm.Ado.DataType) + { + case DataType.MySql: + case DataType.OdbcMySql: + case DataType.CustomMySql: + if (strlen == -2) colattr.DbType = $"LONGTEXT{strNotNull}"; + else if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; + else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); + break; + case DataType.SqlServer: + case DataType.OdbcSqlServer: + case DataType.CustomSqlServer: + if (strlen < 0) colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1(MAX)"); + else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); + break; + case DataType.PostgreSQL: + case DataType.OdbcPostgreSQL: + case DataType.CustomPostgreSQL: + case DataType.KingbaseES: + case DataType.OdbcKingbaseES: + case DataType.ShenTong: + if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; + else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); + break; + case DataType.Oracle: + case DataType.CustomOracle: + if (strlen < 0) colattr.DbType = $"NCLOB{strNotNull}"; //v1.3.2+ https://github.com/dotnetcore/FreeSql/issues/259 + else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); + break; + case DataType.Dameng: + if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; + else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); + break; + case DataType.OdbcOracle: + case DataType.OdbcDameng: + if (strlen < 0) colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1(4000)"); //ODBC 不支持 NCLOB + else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); + break; + case DataType.Sqlite: + if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; + else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); + break; + case DataType.MsAccess: + charPatten = @"(CHAR|CHAR2|CHARACTER|TEXT)\s*(\([^\)]*\))?"; + if (strlen < 0) colattr.DbType = $"LONGTEXT{strNotNull}"; + else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); + break; + case DataType.Firebird: + charPatten = @"(CHAR|CHAR2|CHARACTER|TEXT)\s*(\([^\)]*\))?"; + if (strlen < 0) colattr.DbType = $"BLOB SUB_TYPE 1{strNotNull}"; + else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); + break; + case DataType.GBase: + if (strlen < 0) colattr.DbType = $"TEXT{strNotNull}"; + else colattr.DbType = Regex.Replace(colattr.DbType, charPatten, $"$1({strlen})"); + break; + } + } + if (colattr.MapType == typeof(string) && colattr.IsVersion == true) colattr.StringLength = 40; + if (colattr.MapType == typeof(byte[]) && colattr.IsVersion == true) colattr.StringLength = 16; // 8=sqlserver timestamp, 16=GuidToBytes + if (colattr.MapType == typeof(byte[]) && colattr.StringLength != 0) + { + int strlen = colattr.StringLength; + var bytePatten = @"(VARBINARY|BINARY|BYTEA)\s*(\([^\)]*\))?"; + var strNotNull = colattr.IsNullable == false ? " NOT NULL" : ""; + switch (common._orm.Ado.DataType) + { + case DataType.MySql: + case DataType.OdbcMySql: + case DataType.CustomMySql: + if (strlen == -2) colattr.DbType = $"LONGBLOB{strNotNull}"; + else if (strlen < 0) colattr.DbType = $"BLOB{strNotNull}"; + else colattr.DbType = Regex.Replace(colattr.DbType, bytePatten, $"$1({strlen})"); + break; + case DataType.SqlServer: + case DataType.OdbcSqlServer: + case DataType.CustomSqlServer: + if (strlen < 0) colattr.DbType = Regex.Replace(colattr.DbType, bytePatten, $"$1(MAX)"); + else colattr.DbType = Regex.Replace(colattr.DbType, bytePatten, $"$1({strlen})"); + break; + case DataType.PostgreSQL: + case DataType.OdbcPostgreSQL: + case DataType.CustomPostgreSQL: + case DataType.KingbaseES: + case DataType.OdbcKingbaseES: + case DataType.ShenTong: //驱动引发的异常:“System.Data.OscarClient.OscarException”(位于 System.Data.OscarClient.dll 中) + colattr.DbType = $"BYTEA{strNotNull}"; //变长二进制串 + break; + case DataType.Oracle: + case DataType.CustomOracle: + colattr.DbType = $"BLOB{strNotNull}"; + break; + case DataType.Dameng: + colattr.DbType = $"BLOB{strNotNull}"; + break; + case DataType.OdbcOracle: + case DataType.OdbcDameng: + colattr.DbType = $"BLOB{strNotNull}"; + break; + case DataType.Sqlite: + colattr.DbType = $"BLOB{strNotNull}"; + break; + case DataType.MsAccess: + if (strlen < 0) colattr.DbType = $"BLOB{strNotNull}"; + else colattr.DbType = Regex.Replace(colattr.DbType, bytePatten, $"$1({strlen})"); + break; + case DataType.Firebird: + colattr.DbType = $"BLOB{strNotNull}"; + break; + case DataType.GBase: + colattr.DbType = $"BYTE{strNotNull}"; + break; + } + } + if (colattr.MapType.NullableTypeOrThis() == typeof(decimal) && (colattr.Precision > 0 || colattr.Scale > 0)) + { + if (colattr.Precision <= 0) colattr.Precision = 10; + if (colattr.Scale <= 0) colattr.Scale = 0; + var decimalPatten = @"(DECIMAL|NUMERIC|NUMBER)\s*(\([^\)]*\))?"; + colattr.DbType = Regex.Replace(colattr.DbType, decimalPatten, $"$1({colattr.Precision},{colattr.Scale})"); + } + + if (trytb.Columns.ContainsKey(colattr.Name)) throw new Exception(CoreStrings.Duplicate_ColumnAttribute(colattr.Name)); + if (trytb.ColumnsByCs.ContainsKey(csName)) throw new Exception(CoreStrings.Duplicate_PropertyName(csName)); + + return col; + } + public static void AuditTableInfo(TableInfo trytb, TableAttribute tbattr, IEnumerable indexes, List columnsList, CommonUtils common) + { + trytb.VersionColumn = trytb.Columns.Values.Where(a => a.Attribute.IsVersion == true).LastOrDefault(); + if (trytb.VersionColumn != null) + { + if (trytb.VersionColumn.Attribute.MapType.IsNullableType() || + trytb.VersionColumn.Attribute.MapType.IsNumberType() == false && !new[] { typeof(byte[]), typeof(string) }.Contains(trytb.VersionColumn.Attribute.MapType)) + throw new Exception(CoreStrings.Properties_AsRowLock_Must_Numeric_Byte(trytb.VersionColumn.CsName)); + } + tbattr?.ParseAsTable(trytb); + + var indexesDict = new Dictionary(StringComparer.CurrentCultureIgnoreCase); + //从数据库查找主键、自增、索引 + if (common.CodeFirst.IsConfigEntityFromDbFirst) + { + try + { + if (common._orm.DbFirst != null) + { + if (common.dbTables == null) + lock (common.dbTablesLock) + if (common.dbTables == null) + common.dbTables = common._orm.DbFirst.GetTablesByDatabase(); + + var finddbtbs = common.dbTables.Where(a => string.Compare(a.Name, trytb.CsName, true) == 0 || string.Compare(a.Name, trytb.DbName, true) == 0); + foreach (var dbtb in finddbtbs) + { + foreach (var dbident in dbtb.Identitys) + { + if (trytb.Columns.TryGetValue(dbident.Name, out var trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbident.CsType.NullableTypeOrThis() || + trytb.ColumnsByCs.TryGetValue(dbident.Name, out trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbident.CsType.NullableTypeOrThis()) + trycol.Attribute.IsIdentity = true; + } + foreach (var dbpk in dbtb.Primarys) + { + if (trytb.Columns.TryGetValue(dbpk.Name, out var trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbpk.CsType.NullableTypeOrThis() || + trytb.ColumnsByCs.TryGetValue(dbpk.Name, out trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbpk.CsType.NullableTypeOrThis()) + trycol.Attribute.IsPrimary = true; + } + foreach (var dbidx in dbtb.IndexesDict) + { + var indexColumns = new List(); + foreach (var dbcol in dbidx.Value.Columns) + { + if (trytb.Columns.TryGetValue(dbcol.Column.Name, out var trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbcol.Column.CsType.NullableTypeOrThis() || + trytb.ColumnsByCs.TryGetValue(dbcol.Column.Name, out trycol) && trycol.Attribute.MapType.NullableTypeOrThis() == dbcol.Column.CsType.NullableTypeOrThis()) + indexColumns.Add(new IndexColumnInfo + { + Column = trycol, + IsDesc = dbcol.IsDesc + }); + } + if (indexColumns.Any() == false) continue; + if (indexesDict.ContainsKey(dbidx.Key)) indexesDict.Remove(dbidx.Key); + indexesDict.Add(dbidx.Key, new IndexInfo + { + Name = dbidx.Key, + Columns = indexColumns.ToArray(), + IsUnique = dbidx.Value.IsUnique, + IndexMethod = IndexMethod.B_Tree + }); + } + } + } + } + catch { } + } + //索引和唯一键 + foreach (var index in indexes) + { + var val = index.Fields?.Trim(' ', '\t', ','); + if (string.IsNullOrEmpty(val)) continue; + var arr = val.Split(',').Select(a => a.Trim(' ', '\t').Trim()).Where(a => !string.IsNullOrEmpty(a)).ToArray(); + if (arr.Any() == false) continue; + var indexColumns = new List(); + foreach (var field in arr) + { + var idxcol = new IndexColumnInfo(); + if (field.EndsWith(" DESC", StringComparison.CurrentCultureIgnoreCase)) idxcol.IsDesc = true; + var colname = Regex.Replace(field, " (DESC|ASC)", "", RegexOptions.IgnoreCase); + if (trytb.ColumnsByCs.TryGetValue(colname, out var trycol) || trytb.Columns.TryGetValue(colname, out trycol)) + { + idxcol.Column = trycol; + indexColumns.Add(idxcol); + } + } + if (indexColumns.Any() == false) continue; + var indexName = common.CodeFirst.IsSyncStructureToLower ? index.Name.ToLower() : (common.CodeFirst.IsSyncStructureToUpper ? index.Name.ToUpper() : index.Name); + if (indexesDict.ContainsKey(indexName)) indexesDict.Remove(indexName); + indexesDict.Add(indexName, new IndexInfo + { + Name = indexName, + Columns = indexColumns.ToArray(), + IsUnique = index.IsUnique, + IndexMethod = index.IndexMethod + }); + } + trytb.Indexes = indexesDict.Values.ToArray(); + + trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsPrimary == true).ToArray(); + if (trytb.Primarys.Any() == false) + { + trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute._IsPrimary == null && string.Compare(a.Attribute.Name, "id", true) == 0).ToArray(); + if (trytb.Primarys.Any() == false) + { + var identcol = trytb.Columns.Values.Where(a => a.Attribute.IsIdentity == true).FirstOrDefault(); + if (identcol != null) trytb.Primarys = new[] { identcol }; + if (trytb.Primarys.Any() == false) + { + trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute._IsPrimary == null && string.Compare(a.Attribute.Name, $"{trytb.DbName}id", true) == 0).ToArray(); + if (trytb.Primarys.Any() == false) + { + trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute._IsPrimary == null && string.Compare(a.Attribute.Name, $"{trytb.DbName}_id", true) == 0).ToArray(); + } + } + } + foreach (var col in trytb.Primarys) + col.Attribute.IsPrimary = true; + } + foreach (var col in trytb.Primarys) + { + col.Attribute.IsNullable = false; + col.Attribute.DbType = col.Attribute.DbType.Replace("NOT NULL", "").Replace(" NULL", "").Trim(); + switch (common._orm.Ado.DataType) + { + case DataType.Sqlite: + col.Attribute.DbType += " NOT NULL"; //sqlite 主键也可以插入 null + break; + } + } + foreach (var col in trytb.Columns.Values) + { + if (col.Attribute.IsPrimary == false && col.Attribute.IsIdentity) col.Attribute.CanUpdate = false; + var ltp = @"\(([^\)]+)\)"; + col.DbTypeText = Regex.Replace(col.Attribute.DbType.Replace("NOT NULL", "").Replace(" NULL", "").Trim(), ltp, ""); + var m = Regex.Match(col.Attribute.DbType, ltp); + if (m.Success == false) continue; + var sizeStr = m.Groups[1].Value.Trim(); + if (sizeStr.EndsWith(" BYTE") || sizeStr.EndsWith(" CHAR")) sizeStr = sizeStr.Remove(sizeStr.Length - 5); //ORACLE + if (string.Compare(sizeStr, "max", true) == 0) + { + col.DbSize = -1; + continue; + } + var sizeArr = sizeStr.Split(','); + if (int.TryParse(sizeArr[0].Trim(), out var size) == false) continue; + if (col.Attribute.MapType.NullableTypeOrThis() == typeof(DateTime)) + { + col.DbScale = (byte)size; + if (col.Attribute.Scale <= 0) col.Attribute.Scale = col.DbScale; + continue; + } + if (sizeArr.Length == 1) + { + col.DbSize = size; + if (col.Attribute.StringLength <= 0) col.Attribute.StringLength = col.DbSize; + continue; + } + if (byte.TryParse(sizeArr[1], out var scale) == false) continue; + col.DbPrecision = (byte)size; + col.DbScale = scale; + if (col.Attribute.Precision <= 0) + { + col.Attribute.Precision = col.DbPrecision; + col.Attribute.Scale = col.DbScale; + } + } + trytb.IsRereadSql = trytb.Columns.Where(a => string.IsNullOrWhiteSpace(a.Value.Attribute.RereadSql) == false).Any(); + trytb.ColumnsByPosition = columnsList.Where(a => a.Attribute.Position > 0).OrderBy(a => a.Attribute.Position) + .Concat(columnsList.Where(a => a.Attribute.Position == 0)) + .Concat(columnsList.Where(a => a.Attribute.Position < 0).OrderBy(a => a.Attribute.Position)).ToArray(); + trytb.ColumnsByCanUpdateDbUpdateValue = columnsList.Where(a => a.Attribute.CanUpdate == true && string.IsNullOrEmpty(a.DbUpdateValue) == false).ToArray(); + } public static void AddTableRef(CommonUtils common, TableInfo trytb, PropertyInfo pnv, bool isLazy, NativeTuple vp, StringBuilder cscode) { var getMethod = vp?.Item4;