using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace SqlSugar { public class UpdateableProvider : IUpdateable where T : class, new() { public SqlSugarClient Context { get; internal set; } public EntityInfo EntityInfo { get; internal set; } public ISqlBuilder SqlBuilder { get; internal set; } public UpdateBuilder UpdateBuilder { get; set; } public IAdo Ado { get { return Context.Ado; } } public T[] UpdateObjs { get; set; } public bool IsMappingTable { get { return this.Context.MappingTables != null && this.Context.MappingTables.Any(); } } public bool IsMappingColumns { get { return this.Context.MappingColumns != null && this.Context.MappingColumns.Any(); } } public bool IsSingle { get { return this.UpdateObjs.Length == 1; } } public List MappingColumnList { get; set; } private List IgnoreColumnNameList { get; set; } private List WhereColumnList { get; set; } private bool IsOffIdentity { get; set; } public MappingTableList OldMappingTableList { get; set; } public bool IsAs { get; set; } public virtual int ExecuteCommand() { PreToSql(); AutoRemoveDataCache(); Check.Exception(UpdateBuilder.WhereValues.IsNullOrEmpty() && GetPrimaryKeys().IsNullOrEmpty(), "You cannot have no primary key and no conditions"); var sql = UpdateBuilder.ToSqlString(); RestoreMapping(); return this.Ado.ExecuteCommand(sql, UpdateBuilder.Parameters?.ToArray()); } public bool ExecuteCommandHasChange() { return this.ExecuteCommand() > 0; } public Task ExecuteCommandAsync() { var result = new Task(() => { var asyncUpdateable = CopyUpdateable(); return asyncUpdateable.ExecuteCommand(); }); TaskStart(result); return result; } public Task ExecuteCommandHasChangeAsync() { var result = new Task(() => { var asyncUpdateable = CopyUpdateable(); return asyncUpdateable.ExecuteCommand() > 0; }); TaskStart(result); return result; } public IUpdateable AS(string tableName) { var entityName = typeof(T).Name; IsAs = true; OldMappingTableList = this.Context.MappingTables; this.Context.MappingTables = this.Context.Utilities.TranslateCopy(this.Context.MappingTables); this.Context.MappingTables.Add(entityName, tableName); return this; ; } public IUpdateable IgnoreColumns(Func ignoreColumMethod) { this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => !ignoreColumMethod?.Invoke(it.PropertyName) ?? default(bool)).ToList(); return this; } public IUpdateable IgnoreColumns(Expression> columns) { var ignoreColumns = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList(); this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => !ignoreColumns.Contains(it.PropertyName)).ToList(); return this; } public IUpdateable ReSetValue(Expression> setValueExpression) { Check.Exception(!IsSingle, "Batch operation not supported ReSetValue"); var expResult = UpdateBuilder.GetExpressionValue(setValueExpression, ResolveExpressType.WhereSingle); var resultString = Regex.Match(expResult.GetResultString(), @"\((.+)\)").Groups[1].Value; var lambda = setValueExpression as LambdaExpression; var expression = lambda.Body; Check.Exception(!(expression is BinaryExpression), "Expression format error"); var leftExpression = (expression as BinaryExpression).Left; Check.Exception(!(leftExpression is MemberExpression), "Expression format error"); var leftResultString = UpdateBuilder.GetExpressionValue(leftExpression, ResolveExpressType.WhereSingle).GetString(); UpdateBuilder.SetValues.Add(new KeyValuePair(leftResultString, resultString)); return this; } private void AutoRemoveDataCache() { var moreSetts = this.Context.CurrentConnectionConfig.MoreSettings; var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices; if (moreSetts != null && moreSetts.IsAutoRemoveDataCache && extService!=null&& extService.DataInfoCacheService!=null) { this.RemoveDataCache(); } } public KeyValuePair> ToSql() { PreToSql(); var sql = UpdateBuilder.ToSqlString(); RestoreMapping(); return new KeyValuePair>(sql, UpdateBuilder.Parameters); } public IUpdateable WhereColumns(Expression> columns) { var whereColumns = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList(); if (this.WhereColumnList == null) this.WhereColumnList = new List(); foreach (var item in whereColumns) { this.WhereColumnList.Add(item); } return this; } public IUpdateable UpdateColumns(Expression> columns) { var updateColumns = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList(); var primaryKeys = GetPrimaryKeys(); foreach (var item in this.UpdateBuilder.DbColumnInfoList) { var mappingInfo = primaryKeys.SingleOrDefault(i => item.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase)); if (mappingInfo != null && mappingInfo.Any()) { item.IsPrimarykey = true; } } this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => updateColumns.Any(uc => uc.Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase) || uc.Equals(it.DbColumnName, StringComparison.CurrentCultureIgnoreCase)) || it.IsPrimarykey || it.IsIdentity).ToList(); return this; } 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), "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 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).ToList(); return this; } public IUpdateable UpdateColumns(Func updateColumMethod) { var primaryKeys = GetPrimaryKeys(); foreach (var item in this.UpdateBuilder.DbColumnInfoList) { var mappingInfo = primaryKeys.SingleOrDefault(i => item.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase)); if (mappingInfo != null && mappingInfo.Any()) { item.IsPrimarykey = true; } } this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => (updateColumMethod?.Invoke(it.PropertyName) ?? default(bool)) || it.IsPrimarykey || it.IsIdentity).ToList(); return this; } public IUpdateable UpdateColumns(Expression> columns) { var expResult = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.Update); var resultArray = expResult.GetResultArray(); Check.ArgumentNullException(resultArray, "UpdateColumns Parameter error, UpdateColumns(it=>new T{ it.id=1}) is valid, UpdateColumns(it=>T) is error"); if (resultArray.HasValue()) { foreach (var item in resultArray) { var key = SqlBuilder.GetNoTranslationColumnName(item); 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).ToList(); return this; } public IUpdateable Where(bool isUpdateNull, bool IsOffIdentity = false) { UpdateBuilder.IsOffIdentity = IsOffIdentity; if (this.UpdateBuilder.LambdaExpressions == null) this.UpdateBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.Context.CurrentConnectionConfig); this.UpdateBuilder.IsNoUpdateNull = isUpdateNull; return this; } public IUpdateable Where(Expression> expression) { var expResult = UpdateBuilder.GetExpressionValue(expression, ResolveExpressType.WhereSingle); UpdateBuilder.WhereValues.Add(expResult.GetResultString()); return this; } public IUpdateable With(string lockString) { if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) this.UpdateBuilder.TableWithString = lockString; return this; } public IUpdateable RemoveDataCache() { var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService; CacheSchemeMain.RemoveCache(cacheService, this.Context.EntityMaintenance.GetTableName()); return this; } internal void Init() { this.UpdateBuilder.TableName = EntityInfo.EntityName; if (IsMappingTable) { var mappingInfo = this.Context.MappingTables.SingleOrDefault(it => it.EntityName == EntityInfo.EntityName); if (mappingInfo != null) { this.UpdateBuilder.TableName = mappingInfo.DbTableName; } } Check.Exception(UpdateObjs == null || UpdateObjs.Count() == 0, "UpdateObjs is null"); var i = 0; foreach (var item in UpdateObjs) { var updateItem = new List(); foreach (var column in EntityInfo.Columns) { var columnInfo = new DbColumnInfo { Value = column.PropertyInfo.GetValue(item, null), DbColumnName = GetDbColumnName(column.PropertyName), PropertyName = column.PropertyName, PropertyType = UtilMethods.GetUnderType(column.PropertyInfo), TableId = i }; if (columnInfo.PropertyType.IsEnum()) { columnInfo.Value = Convert.ToInt64(columnInfo.Value); } updateItem.Add(columnInfo); } this.UpdateBuilder.DbColumnInfoList.AddRange(updateItem); ++i; } } private void PreToSql() { UpdateBuilder.PrimaryKeys = GetPrimaryKeys(); #region IgnoreColumns if (this.Context.IgnoreColumns != null && this.Context.IgnoreColumns.Any()) { var currentIgnoreColumns = this.Context.IgnoreColumns.Where(it => it.EntityName == this.EntityInfo.EntityName).ToList(); this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => { return !currentIgnoreColumns.Any(i => it.PropertyName.Equals(i.PropertyName, StringComparison.CurrentCulture)); }).ToList(); } #endregion if (this.IsSingle) { 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)) { continue; } this.UpdateBuilder.Parameters.Add(new SugarParameter(this.SqlBuilder.SqlParameterKeyWord + item.DbColumnName, item.Value, item.PropertyType)); } } #region Identities var identities = GetIdentityKeys(); if (identities != null && identities.Any()) { this.UpdateBuilder.DbColumnInfoList.ForEach(it => { var mappingInfo = identities.SingleOrDefault(i => it.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase)); if (mappingInfo != null && mappingInfo.Any()) { it.IsIdentity = true; } }); } #endregion var primaryKey = GetPrimaryKeys(); if (primaryKey != null && primaryKey.Count > 0) { this.UpdateBuilder.DbColumnInfoList.ForEach(it => { var mappingInfo = primaryKey.SingleOrDefault(i => it.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase)); if (mappingInfo != null && mappingInfo.Any()) { it.IsPrimarykey = true; } }); } if (this.UpdateBuilder.Parameters.HasValue() && this.UpdateBuilder.SetValues.IsValuable()) { this.UpdateBuilder.Parameters.RemoveAll(it => this.UpdateBuilder.SetValues.Any(v => (SqlBuilder.SqlParameterKeyWord + SqlBuilder.GetNoTranslationColumnName(v.Key)) == it.ParameterName)); } } private string GetDbColumnName(string propertyName) { if (!IsMappingColumns) { return propertyName; } if (this.Context.MappingColumns.Any(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase))) { this.MappingColumnList = this.Context.MappingColumns.Where(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase)).ToList(); } if (MappingColumnList == null || !MappingColumnList.Any()) { return propertyName; } else { var mappInfo = this.MappingColumnList.FirstOrDefault(it => it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase)); return mappInfo == null ? propertyName : mappInfo.DbColumnName; } } private List GetPrimaryKeys() { if (this.WhereColumnList.HasValue()) { return this.WhereColumnList; } if (this.Context.IsSystemTablesConfig) { return this.Context.DbMaintenance.GetPrimaries(this.Context.EntityMaintenance.GetTableName(this.EntityInfo.EntityName)); } else { return this.EntityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList(); } } protected virtual List GetIdentityKeys() { if (this.Context.IsSystemTablesConfig) { return this.Context.DbMaintenance.GetIsIdentities(this.Context.EntityMaintenance.GetTableName(this.EntityInfo.EntityName)); } else { return this.EntityInfo.Columns.Where(it => it.IsIdentity).Select(it => it.DbColumnName).ToList(); } } private void RestoreMapping() { if (IsAs) { this.Context.MappingTables = OldMappingTableList; } } private void TaskStart(Task result) { if (this.Context.CurrentConnectionConfig.IsShardSameThread) { Check.Exception(true, "IsShardSameThread=true can't be used async method"); } result.Start(); } private IUpdateable CopyUpdateable() { var asyncContext = this.Context.Utilities.CopyContext(true); asyncContext.CurrentConnectionConfig.IsAutoCloseConnection = true; var asyncUpdateable = asyncContext.Updateable(this.UpdateObjs); var asyncUpdateableBuilder = asyncUpdateable.UpdateBuilder; asyncUpdateableBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList; asyncUpdateableBuilder.IsNoUpdateNull = this.UpdateBuilder.IsNoUpdateNull; asyncUpdateableBuilder.Parameters = this.UpdateBuilder.Parameters; asyncUpdateableBuilder.sql = this.UpdateBuilder.sql; asyncUpdateableBuilder.WhereValues = this.UpdateBuilder.WhereValues; asyncUpdateableBuilder.TableWithString = this.UpdateBuilder.TableWithString; asyncUpdateableBuilder.TableName = this.UpdateBuilder.TableName; asyncUpdateableBuilder.PrimaryKeys = this.UpdateBuilder.PrimaryKeys; asyncUpdateableBuilder.IsOffIdentity = this.UpdateBuilder.IsOffIdentity; asyncUpdateableBuilder.SetValues = this.UpdateBuilder.SetValues; return asyncUpdateable; } } }