diff --git a/Src/Asp.Net/SqlServerTest/Config.cs b/Src/Asp.Net/SqlServerTest/Config.cs index 43f3df924..7d87d0ee6 100644 --- a/Src/Asp.Net/SqlServerTest/Config.cs +++ b/Src/Asp.Net/SqlServerTest/Config.cs @@ -8,8 +8,8 @@ namespace OrmTest { public class Config { - public static string ConnectionString = "server=.;uid=sa;pwd=@jhl85661501;database=SqlSugar4XTest"; - public static string ConnectionString2 = "server=.;uid=sa;pwd=@jhl85661501;database=SQLSUGAR4XTEST"; - public static string ConnectionString3 = "server=.;uid=sa;pwd=@jhl85661501;database=sqlsugar4xtest"; + public static string ConnectionString = "server=.;uid=sa;pwd=sasa;database=SqlSugar4XTest"; + public static string ConnectionString2 = "server=.;uid=sa;pwd=sasa;database=SQLSUGAR4XTEST"; + public static string ConnectionString3 = "server=.;uid=sa;pwd=sasa;database=sqlsugar4xtest"; } } diff --git a/Src/Asp.Net/SqlServerTest/Demos/F_VersionValidation.cs b/Src/Asp.Net/SqlServerTest/Demos/F_VersionValidation.cs new file mode 100644 index 000000000..1b3eae99f --- /dev/null +++ b/Src/Asp.Net/SqlServerTest/Demos/F_VersionValidation.cs @@ -0,0 +1,55 @@ +using OrmTest.Demo; +using OrmTest.Models; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OrmTest.Demo +{ + public class VersionValidation : DemoBase + { + public static void Init() + { + var db = GetInstance(); + try + { + for (int i = 0; i < 10; i++) + { + var data = new StudentVersion() + { + Id = db.Queryable().Select(it => it.Id).First(), + CreateTime = DateTime.Now, + Name = "" + }; + db.Updateable(data).AS("student").ExecuteCommand(); + + var time = db.Queryable().Where(it=>it.Id==data.Id).Select(it => it.CreateTime).Single(); + data.CreateTime = time.Value; + db.Updateable(data).AS("student").ExecuteCommand(); + + data.CreateTime = time.Value.AddMilliseconds(-1); + db.Updateable(data).AS("student").ExecuteCommand(); + } + } + catch (Exception ex) + { + if (ex is SqlSugar.VersionExceptions) + { + Console.Write(ex.Message); + } + else { + + } + } + } + + public class StudentVersion + { + public int Id { get; set; } + public string Name { get; set; } + [SqlSugar.SugarColumn(IsEnableUpdateVersionValidation = true)] + public DateTime CreateTime { get; set; } + } + } +} diff --git a/Src/Asp.Net/SqlServerTest/Program.cs b/Src/Asp.Net/SqlServerTest/Program.cs index 1f424bdf3..e4c2cad69 100644 --- a/Src/Asp.Net/SqlServerTest/Program.cs +++ b/Src/Asp.Net/SqlServerTest/Program.cs @@ -51,6 +51,7 @@ namespace OrmTest Demo.ExtSqlFun.Init(); Demo.QueryableView.Init(); Demo.AttributeDemo.Init(); + Demo.VersionValidation.Init(); } } } diff --git a/Src/Asp.Net/SqlServerTest/SqlServerTest.csproj b/Src/Asp.Net/SqlServerTest/SqlServerTest.csproj index 7cb50e568..c6c826100 100644 --- a/Src/Asp.Net/SqlServerTest/SqlServerTest.csproj +++ b/Src/Asp.Net/SqlServerTest/SqlServerTest.csproj @@ -47,6 +47,7 @@ + diff --git a/Src/Asp.Net/SqlSugar/Abstract/EntityMaintenance/EntityMaintenance.cs b/Src/Asp.Net/SqlSugar/Abstract/EntityMaintenance/EntityMaintenance.cs index 495e5e87c..664013689 100644 --- a/Src/Asp.Net/SqlSugar/Abstract/EntityMaintenance/EntityMaintenance.cs +++ b/Src/Asp.Net/SqlSugar/Abstract/EntityMaintenance/EntityMaintenance.cs @@ -158,6 +158,7 @@ namespace SqlSugar column.DecimalDigits = sugarColumn.DecimalDigits; column.OracleSequenceName = sugarColumn.OracleSequenceName; column.IsOnlyIgnoreInsert = sugarColumn.IsOnlyIgnoreInsert; + column.IsEnableUpdateVersionValidation = sugarColumn.IsEnableUpdateVersionValidation; } else { diff --git a/Src/Asp.Net/SqlSugar/Abstract/UpdateProvider/UpdateableProvider.cs b/Src/Asp.Net/SqlSugar/Abstract/UpdateProvider/UpdateableProvider.cs index 13e3eb167..6f16ce0f4 100644 --- a/Src/Asp.Net/SqlSugar/Abstract/UpdateProvider/UpdateableProvider.cs +++ b/Src/Asp.Net/SqlSugar/Abstract/UpdateProvider/UpdateableProvider.cs @@ -31,9 +31,11 @@ namespace SqlSugar AutoRemoveDataCache(); Check.Exception(UpdateBuilder.WhereValues.IsNullOrEmpty() && GetPrimaryKeys().IsNullOrEmpty(), "You cannot have no primary key and no conditions"); string sql = UpdateBuilder.ToSqlString(); + ValidateVersion(); RestoreMapping(); return this.Ado.ExecuteCommand(sql, UpdateBuilder.Parameters == null ? null : UpdateBuilder.Parameters.ToArray()); } + public bool ExecuteCommandHasChange() { return this.ExecuteCommand() > 0; @@ -64,7 +66,8 @@ namespace SqlSugar IsAs = true; OldMappingTableList = this.Context.MappingTables; this.Context.MappingTables = this.Context.Utilities.TranslateCopy(this.Context.MappingTables); - if (this.Context.MappingTables.Any(it => it.EntityName == entityName)) { + if (this.Context.MappingTables.Any(it => it.EntityName == entityName)) + { this.Context.MappingTables.Add(this.Context.MappingTables.First(it => it.EntityName == entityName).DbTableName, tableName); } this.Context.MappingTables.Add(entityName, tableName); @@ -110,7 +113,7 @@ namespace SqlSugar { var moreSetts = this.Context.CurrentConnectionConfig.MoreSettings; var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; - if (moreSetts != null && moreSetts.IsAutoRemoveDataCache && extService!=null&& extService.DataInfoCacheService!=null) + if (moreSetts != null && moreSetts.IsAutoRemoveDataCache && extService != null && extService.DataInfoCacheService != null) { this.RemoveDataCache(); } @@ -150,12 +153,13 @@ namespace SqlSugar return this; } - public IUpdateable UpdateColumns(Expression> columns) { + public IUpdateable UpdateColumns(Expression> columns) + { var binaryExp = columns.Body as BinaryExpression; Check.Exception(!binaryExp.NodeType.IsIn(ExpressionType.Equal), "No support {0}", columns.ToString()); - Check.Exception(!(binaryExp.Left is MemberExpression)&& !(binaryExp.Left is UnaryExpression), "No support {0}", columns.ToString()); + Check.Exception(!(binaryExp.Left is MemberExpression) && !(binaryExp.Left is UnaryExpression), "No support {0}", columns.ToString()); Check.Exception(ExpressionTool.IsConstExpression(binaryExp.Left as MemberExpression), "No support {0}", columns.ToString()); - var expResult = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.WhereSingle).GetResultString().Replace("))",") )").Replace("((", "( (").Trim().TrimStart('(').TrimEnd(')'); + var expResult = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.WhereSingle).GetResultString().Replace("))", ") )").Replace("((", "( (").Trim().TrimStart('(').TrimEnd(')'); string key = SqlBuilder.GetNoTranslationColumnName(expResult); UpdateBuilder.SetValues.Add(new KeyValuePair(SqlBuilder.GetTranslationColumnName(key), expResult)); this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => UpdateBuilder.SetValues.Any(v => SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey == true).ToList(); @@ -189,7 +193,7 @@ namespace SqlSugar UpdateBuilder.SetValues.Add(new KeyValuePair(SqlBuilder.GetTranslationColumnName(key), item)); } } - this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => UpdateBuilder.SetValues.Any(v => SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.DbColumnName,StringComparison.CurrentCultureIgnoreCase)|| SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.PropertyName,StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey == true).ToList(); + this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => UpdateBuilder.SetValues.Any(v => SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase) || SqlBuilder.GetNoTranslationColumnName(v.Key).Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey == true).ToList(); return this; } [Obsolete("Use IUpdateable IgnoreColumns(bool ignoreAllNullColumns, bool isOffIdentity = false);")] @@ -211,20 +215,22 @@ namespace SqlSugar public IUpdateable Where(string whereSql, object parameters = null) { - if (whereSql.HasValue()) { + if (whereSql.HasValue()) + { UpdateBuilder.WhereValues.Add(whereSql); } - if (parameters != null) { + if (parameters != null) + { UpdateBuilder.Parameters.AddRange(Context.Ado.GetParameters(parameters)); } return this; } - public IUpdateable Where(string fieldName,string conditionalType, object fieldValue) + public IUpdateable Where(string fieldName, string conditionalType, object fieldValue) { - var whereSql=this.SqlBuilder.GetWhere(fieldName, conditionalType,0); + var whereSql = this.SqlBuilder.GetWhere(fieldName, conditionalType, 0); this.Where(whereSql); - string parameterName = this.SqlBuilder.SqlParameterKeyWord + fieldName+ "0"; + string parameterName = this.SqlBuilder.SqlParameterKeyWord + fieldName + "0"; this.UpdateBuilder.Parameters.Add(new SugarParameter(parameterName, fieldValue)); return this; } @@ -259,7 +265,7 @@ namespace SqlSugar foreach (var item in UpdateObjs) { List updateItem = new List(); - var isDic = item is Dictionary; + var isDic = item is Dictionary; if (isDic) { SetUpdateItemByDic(i, item, updateItem); @@ -273,14 +279,14 @@ namespace SqlSugar } private void SetUpdateItemByDic(int i, T item, List updateItem) { - foreach (var column in item as Dictionary) + foreach (var column in item as Dictionary) { var columnInfo = new DbColumnInfo() { Value = column.Value, - DbColumnName =column.Key, + DbColumnName = column.Key, PropertyName = column.Key, - PropertyType = column.Value==null?DBNull.Value.GetType():UtilMethods.GetUnderType(column.Value.GetType()), + PropertyType = column.Value == null ? DBNull.Value.GetType() : UtilMethods.GetUnderType(column.Value.GetType()), TableId = i }; if (columnInfo.PropertyType.IsEnum()) @@ -331,7 +337,8 @@ namespace SqlSugar foreach (var item in this.UpdateBuilder.DbColumnInfoList) { if (this.UpdateBuilder.Parameters == null) this.UpdateBuilder.Parameters = new List(); - if (this.UpdateBuilder.SetValues.Any(it =>this.SqlBuilder.GetNoTranslationColumnName(it.Key) == item.PropertyName)) { + if (this.UpdateBuilder.SetValues.Any(it => this.SqlBuilder.GetNoTranslationColumnName(it.Key) == item.PropertyName)) + { continue; } this.UpdateBuilder.Parameters.Add(new SugarParameter(this.SqlBuilder.SqlParameterKeyWord + item.DbColumnName, item.Value, item.PropertyType)); @@ -449,5 +456,54 @@ namespace SqlSugar asyncUpdateableBuilder.SetValues = this.UpdateBuilder.SetValues; return asyncUpdateable; } + + private void ValidateVersion() + { + var versionColumn = this.EntityInfo.Columns.FirstOrDefault(it => it.IsEnableUpdateVersionValidation); + if (versionColumn != null) + { + var pks = this.UpdateBuilder.DbColumnInfoList.Where(it => it.IsPrimarykey).ToList(); + List conModels = new List(); + foreach (var item in pks) + { + conModels.Add(new ConditionalModel() { FieldName = item.DbColumnName, ConditionalType = ConditionalType.Equal, FieldValue = item.Value.ObjToString() }); + } + var dbInfo = this.Context.Queryable().Where(conModels).First(); + if (dbInfo != null) + { + var currentVersion = this.EntityInfo.Type.GetProperty(versionColumn.PropertyName).GetValue(UpdateObjs.Last(), null); + var dbVersion = this.EntityInfo.Type.GetProperty(versionColumn.PropertyName).GetValue(dbInfo, null); + if (currentVersion == null) + currentVersion = UtilMethods.DefaultForType(versionColumn.PropertyInfo.PropertyType); + if (dbVersion == null) + dbVersion = UtilMethods.DefaultForType(versionColumn.PropertyInfo.PropertyType); + if (versionColumn.PropertyInfo.PropertyType.IsIn(UtilConstants.IntType, UtilConstants.LongType)) + { + if (Convert.ToInt64(dbVersion) > Convert.ToInt64(currentVersion)) + { + throw new VersionExceptions(string.Format("UpdateVersionValidation {0} Not the latest version ", versionColumn.PropertyName)); + } + } + else if (versionColumn.PropertyInfo.PropertyType.IsIn(UtilConstants.DateType)) + { + if (dbVersion.ObjToDate() > currentVersion.ObjToDate()) + { + throw new VersionExceptions(string.Format("UpdateVersionValidation {0} Not the latest version ", versionColumn.PropertyName)); + } + } + else if (versionColumn.PropertyInfo.PropertyType.IsIn(UtilConstants.ByteArrayType)) + { + if (UtilMethods.IsBigOne((byte[])dbVersion, (byte[])currentVersion)) + { + throw new VersionExceptions(string.Format("UpdateVersionValidation {0} Not the latest version ", versionColumn.PropertyName)); + } + } + else + { + Check.ThrowNotSupportedException(string.Format("UpdateVersionValidation Not Supported Type [ {0} ] , {1}", versionColumn.PropertyInfo.PropertyType, versionColumn.PropertyName)); + } + } + } + } } } diff --git a/Src/Asp.Net/SqlSugar/Entities/EntityColumnInfo.cs b/Src/Asp.Net/SqlSugar/Entities/EntityColumnInfo.cs index 962ea2264..c35cb9dab 100644 --- a/Src/Asp.Net/SqlSugar/Entities/EntityColumnInfo.cs +++ b/Src/Asp.Net/SqlSugar/Entities/EntityColumnInfo.cs @@ -19,6 +19,7 @@ namespace SqlSugar public bool IsNullable { get; set; } public bool IsIdentity { get; set; } public bool IsPrimarykey { get; set; } + public bool IsEnableUpdateVersionValidation { get; set; } public string EntityName { get; set; } public string DbTableName { get; set; } public bool IsIgnore { get; set; } diff --git a/Src/Asp.Net/SqlSugar/Entities/Mapping/SugarMappingAttribute.cs b/Src/Asp.Net/SqlSugar/Entities/Mapping/SugarMappingAttribute.cs index b9200945f..a44d15660 100644 --- a/Src/Asp.Net/SqlSugar/Entities/Mapping/SugarMappingAttribute.cs +++ b/Src/Asp.Net/SqlSugar/Entities/Mapping/SugarMappingAttribute.cs @@ -105,6 +105,15 @@ namespace SqlSugar get { return _IsOnlyIgnoreInsert; } set { _IsOnlyIgnoreInsert = value; } } + + + private bool _IsEnableUpdateVersionValidation; + public bool IsEnableUpdateVersionValidation { + get { return _IsEnableUpdateVersionValidation; } + set { _IsEnableUpdateVersionValidation = value; } + } + + } } diff --git a/Src/Asp.Net/SqlSugar/Utilities/UtilExceptions.cs b/Src/Asp.Net/SqlSugar/Utilities/UtilExceptions.cs index a4597317a..f7c34d994 100644 --- a/Src/Asp.Net/SqlSugar/Utilities/UtilExceptions.cs +++ b/Src/Asp.Net/SqlSugar/Utilities/UtilExceptions.cs @@ -52,4 +52,9 @@ namespace SqlSugar return string.Format("{0} : '{1}' \r\n", key, value); } } + public class VersionExceptions : UtilExceptions + { + public VersionExceptions(string message) + : base(message){ } + } } diff --git a/Src/Asp.Net/SqlSugar/Utilities/UtilMethods.cs b/Src/Asp.Net/SqlSugar/Utilities/UtilMethods.cs index 410d6dd9a..de19333c6 100644 --- a/Src/Asp.Net/SqlSugar/Utilities/UtilMethods.cs +++ b/Src/Asp.Net/SqlSugar/Utilities/UtilMethods.cs @@ -14,7 +14,7 @@ namespace SqlSugar internal static Type GetUnderType(Type oldType) { Type type = Nullable.GetUnderlyingType(oldType); - return type==null ? oldType : type; + return type == null ? oldType : type; } internal static Type GetRootBaseType(Type entityType) @@ -103,5 +103,31 @@ namespace SqlSugar action(); return value; } + + internal static object DefaultForType(Type targetType) + { + return targetType.IsValueType ? Activator.CreateInstance(targetType) : null; + } + + internal static bool IsBigOne(byte[] bytearray1, byte[] bytearray2) + { + int result = 0; + if (bytearray1.Length != bytearray2.Length) + { + result = bytearray1.Length - bytearray2.Length; + } + else + { + for (int i = 0; i < bytearray1.Length; i++) + { + if (bytearray1[i] != bytearray2[i]) + { + result = (int)(bytearray1[i] - bytearray2[i]); + break; + } + } + } + return result<0; + } } }