You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

385 lines
19 KiB

2 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Text.RegularExpressions;
  6. using System.Threading.Tasks;
  7. namespace SqlSugar
  8. {
  9. public class UpdateableProvider<T> : IUpdateable<T> where T : class, new()
  10. {
  11. public SqlSugarClient Context { get; internal set; }
  12. public EntityInfo EntityInfo { get; internal set; }
  13. public ISqlBuilder SqlBuilder { get; internal set; }
  14. public UpdateBuilder UpdateBuilder { get; set; }
  15. public IAdo Ado { get { return Context.Ado; } }
  16. public T[] UpdateObjs { get; set; }
  17. public bool IsMappingTable { get { return this.Context.MappingTables != null && this.Context.MappingTables.Any(); } }
  18. public bool IsMappingColumns { get { return this.Context.MappingColumns != null && this.Context.MappingColumns.Any(); } }
  19. public bool IsSingle { get { return this.UpdateObjs.Length == 1; } }
  20. public List<MappingColumn> MappingColumnList { get; set; }
  21. private List<string> IgnoreColumnNameList { get; set; }
  22. private List<string> WhereColumnList { get; set; }
  23. private bool IsOffIdentity { get; set; }
  24. public MappingTableList OldMappingTableList { get; set; }
  25. public bool IsAs { get; set; }
  26. public virtual int ExecuteCommand()
  27. {
  28. PreToSql();
  29. AutoRemoveDataCache();
  30. Check.Exception(UpdateBuilder.WhereValues.IsNullOrEmpty() && GetPrimaryKeys().IsNullOrEmpty(), "You cannot have no primary key and no conditions");
  31. var sql = UpdateBuilder.ToSqlString();
  32. RestoreMapping();
  33. return this.Ado.ExecuteCommand(sql, UpdateBuilder.Parameters?.ToArray());
  34. }
  35. public bool ExecuteCommandHasChange()
  36. {
  37. return this.ExecuteCommand() > 0;
  38. }
  39. public Task<int> ExecuteCommandAsync()
  40. {
  41. var result = new Task<int>(() =>
  42. {
  43. var asyncUpdateable = CopyUpdateable();
  44. return asyncUpdateable.ExecuteCommand();
  45. });
  46. TaskStart(result);
  47. return result;
  48. }
  49. public Task<bool> ExecuteCommandHasChangeAsync()
  50. {
  51. var result = new Task<bool>(() =>
  52. {
  53. var asyncUpdateable = CopyUpdateable();
  54. return asyncUpdateable.ExecuteCommand() > 0;
  55. });
  56. TaskStart(result);
  57. return result;
  58. }
  59. public IUpdateable<T> AS(string tableName)
  60. {
  61. var entityName = typeof(T).Name;
  62. IsAs = true;
  63. OldMappingTableList = this.Context.MappingTables;
  64. this.Context.MappingTables = this.Context.Utilities.TranslateCopy(this.Context.MappingTables);
  65. this.Context.MappingTables.Add(entityName, tableName);
  66. return this; ;
  67. }
  68. public IUpdateable<T> IgnoreColumns(Func<string, bool> ignoreColumMethod)
  69. {
  70. this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => !ignoreColumMethod?.Invoke(it.PropertyName) ?? default(bool)).ToList();
  71. return this;
  72. }
  73. public IUpdateable<T> IgnoreColumns(Expression<Func<T, object>> columns)
  74. {
  75. var ignoreColumns = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList();
  76. this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => !ignoreColumns.Contains(it.PropertyName)).ToList();
  77. return this;
  78. }
  79. public IUpdateable<T> ReSetValue(Expression<Func<T, bool>> setValueExpression)
  80. {
  81. Check.Exception(!IsSingle, "Batch operation not supported ReSetValue");
  82. var expResult = UpdateBuilder.GetExpressionValue(setValueExpression, ResolveExpressType.WhereSingle);
  83. var resultString = Regex.Match(expResult.GetResultString(), @"\((.+)\)").Groups[1].Value;
  84. var lambda = setValueExpression as LambdaExpression;
  85. var expression = lambda.Body;
  86. Check.Exception(!(expression is BinaryExpression), "Expression format error");
  87. var leftExpression = (expression as BinaryExpression).Left;
  88. Check.Exception(!(leftExpression is MemberExpression), "Expression format error");
  89. var leftResultString = UpdateBuilder.GetExpressionValue(leftExpression, ResolveExpressType.WhereSingle).GetString();
  90. UpdateBuilder.SetValues.Add(new KeyValuePair<string, string>(leftResultString, resultString));
  91. return this;
  92. }
  93. private void AutoRemoveDataCache()
  94. {
  95. var moreSetts = this.Context.CurrentConnectionConfig.MoreSettings;
  96. var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices;
  97. if (moreSetts != null && moreSetts.IsAutoRemoveDataCache && extService!=null&& extService.DataInfoCacheService!=null)
  98. {
  99. this.RemoveDataCache();
  100. }
  101. }
  102. public KeyValuePair<string, List<SugarParameter>> ToSql()
  103. {
  104. PreToSql();
  105. var sql = UpdateBuilder.ToSqlString();
  106. RestoreMapping();
  107. return new KeyValuePair<string, List<SugarParameter>>(sql, UpdateBuilder.Parameters);
  108. }
  109. public IUpdateable<T> WhereColumns(Expression<Func<T, object>> columns)
  110. {
  111. var whereColumns = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList();
  112. if (this.WhereColumnList == null) this.WhereColumnList = new List<string>();
  113. foreach (var item in whereColumns)
  114. {
  115. this.WhereColumnList.Add(item);
  116. }
  117. return this;
  118. }
  119. public IUpdateable<T> UpdateColumns(Expression<Func<T, object>> columns)
  120. {
  121. var updateColumns = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList();
  122. var primaryKeys = GetPrimaryKeys();
  123. foreach (var item in this.UpdateBuilder.DbColumnInfoList)
  124. {
  125. var mappingInfo = primaryKeys.SingleOrDefault(i => item.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase));
  126. if (mappingInfo != null && mappingInfo.Any())
  127. {
  128. item.IsPrimarykey = true;
  129. }
  130. }
  131. 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();
  132. return this;
  133. }
  134. public IUpdateable<T> UpdateColumns(Expression<Func<T, bool>> columns) {
  135. var binaryExp = columns.Body as BinaryExpression;
  136. Check.Exception(!binaryExp.NodeType.IsIn(ExpressionType.Equal), "No support {0}", columns.ToString());
  137. Check.Exception(!(binaryExp.Left is MemberExpression), "No support {0}", columns.ToString());
  138. Check.Exception(ExpressionTool.IsConstExpression(binaryExp.Left as MemberExpression), "No support {0}", columns.ToString());
  139. var expResult = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.WhereSingle).GetResultString().Replace("))",") )").Replace("((", "( (").Trim().TrimStart('(').TrimEnd(')');
  140. var key = SqlBuilder.GetNoTranslationColumnName(expResult);
  141. UpdateBuilder.SetValues.Add(new KeyValuePair<string, string>(SqlBuilder.GetTranslationColumnName(key), expResult));
  142. 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();
  143. return this;
  144. }
  145. public IUpdateable<T> UpdateColumns(Func<string, bool> updateColumMethod)
  146. {
  147. var primaryKeys = GetPrimaryKeys();
  148. foreach (var item in this.UpdateBuilder.DbColumnInfoList)
  149. {
  150. var mappingInfo = primaryKeys.SingleOrDefault(i => item.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase));
  151. if (mappingInfo != null && mappingInfo.Any())
  152. {
  153. item.IsPrimarykey = true;
  154. }
  155. }
  156. this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it => (updateColumMethod?.Invoke(it.PropertyName) ?? default(bool)) || it.IsPrimarykey || it.IsIdentity).ToList();
  157. return this;
  158. }
  159. public IUpdateable<T> UpdateColumns(Expression<Func<T, T>> columns)
  160. {
  161. var expResult = UpdateBuilder.GetExpressionValue(columns, ResolveExpressType.Update);
  162. var resultArray = expResult.GetResultArray();
  163. Check.ArgumentNullException(resultArray, "UpdateColumns Parameter error, UpdateColumns(it=>new T{ it.id=1}) is valid, UpdateColumns(it=>T) is error");
  164. if (resultArray.HasValue())
  165. {
  166. foreach (var item in resultArray)
  167. {
  168. var key = SqlBuilder.GetNoTranslationColumnName(item);
  169. UpdateBuilder.SetValues.Add(new KeyValuePair<string, string>(SqlBuilder.GetTranslationColumnName(key), item));
  170. }
  171. }
  172. 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();
  173. return this;
  174. }
  175. public IUpdateable<T> Where(bool isUpdateNull, bool IsOffIdentity = false)
  176. {
  177. UpdateBuilder.IsOffIdentity = IsOffIdentity;
  178. if (this.UpdateBuilder.LambdaExpressions == null)
  179. this.UpdateBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.Context.CurrentConnectionConfig);
  180. this.UpdateBuilder.IsNoUpdateNull = isUpdateNull;
  181. return this;
  182. }
  183. public IUpdateable<T> Where(Expression<Func<T, bool>> expression)
  184. {
  185. var expResult = UpdateBuilder.GetExpressionValue(expression, ResolveExpressType.WhereSingle);
  186. UpdateBuilder.WhereValues.Add(expResult.GetResultString());
  187. return this;
  188. }
  189. public IUpdateable<T> With(string lockString)
  190. {
  191. if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer)
  192. this.UpdateBuilder.TableWithString = lockString;
  193. return this;
  194. }
  195. public IUpdateable<T> RemoveDataCache()
  196. {
  197. var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService;
  198. CacheSchemeMain.RemoveCache(cacheService, this.Context.EntityMaintenance.GetTableName<T>());
  199. return this;
  200. }
  201. internal void Init()
  202. {
  203. this.UpdateBuilder.TableName = EntityInfo.EntityName;
  204. if (IsMappingTable)
  205. {
  206. var mappingInfo = this.Context.MappingTables.SingleOrDefault(it => it.EntityName == EntityInfo.EntityName);
  207. if (mappingInfo != null)
  208. {
  209. this.UpdateBuilder.TableName = mappingInfo.DbTableName;
  210. }
  211. }
  212. Check.Exception(UpdateObjs == null || UpdateObjs.Count() == 0, "UpdateObjs is null");
  213. var i = 0;
  214. foreach (var item in UpdateObjs)
  215. {
  216. var updateItem = new List<DbColumnInfo>();
  217. foreach (var column in EntityInfo.Columns)
  218. {
  219. var columnInfo = new DbColumnInfo
  220. {
  221. Value = column.PropertyInfo.GetValue(item, null),
  222. DbColumnName = GetDbColumnName(column.PropertyName),
  223. PropertyName = column.PropertyName,
  224. PropertyType = UtilMethods.GetUnderType(column.PropertyInfo),
  225. TableId = i
  226. };
  227. if (columnInfo.PropertyType.IsEnum())
  228. {
  229. columnInfo.Value = Convert.ToInt64(columnInfo.Value);
  230. }
  231. updateItem.Add(columnInfo);
  232. }
  233. this.UpdateBuilder.DbColumnInfoList.AddRange(updateItem);
  234. ++i;
  235. }
  236. }
  237. private void PreToSql()
  238. {
  239. UpdateBuilder.PrimaryKeys = GetPrimaryKeys();
  240. #region IgnoreColumns
  241. if (this.Context.IgnoreColumns != null && this.Context.IgnoreColumns.Any())
  242. {
  243. var currentIgnoreColumns = this.Context.IgnoreColumns.Where(it => it.EntityName == this.EntityInfo.EntityName).ToList();
  244. this.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.Where(it =>
  245. {
  246. return !currentIgnoreColumns.Any(i => it.PropertyName.Equals(i.PropertyName, StringComparison.CurrentCulture));
  247. }).ToList();
  248. }
  249. #endregion
  250. if (this.IsSingle)
  251. {
  252. foreach (var item in this.UpdateBuilder.DbColumnInfoList)
  253. {
  254. if (this.UpdateBuilder.Parameters == null) this.UpdateBuilder.Parameters = new List<SugarParameter>();
  255. if (this.UpdateBuilder.SetValues.Any(it =>this.SqlBuilder.GetNoTranslationColumnName(it.Key) == item.PropertyName)) {
  256. continue;
  257. }
  258. this.UpdateBuilder.Parameters.Add(new SugarParameter(this.SqlBuilder.SqlParameterKeyWord + item.DbColumnName, item.Value, item.PropertyType));
  259. }
  260. }
  261. #region Identities
  262. var identities = GetIdentityKeys();
  263. if (identities != null && identities.Any())
  264. {
  265. this.UpdateBuilder.DbColumnInfoList.ForEach(it =>
  266. {
  267. var mappingInfo = identities.SingleOrDefault(i => it.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase));
  268. if (mappingInfo != null && mappingInfo.Any())
  269. {
  270. it.IsIdentity = true;
  271. }
  272. });
  273. }
  274. #endregion
  275. var primaryKey = GetPrimaryKeys();
  276. if (primaryKey != null && primaryKey.Count > 0)
  277. {
  278. this.UpdateBuilder.DbColumnInfoList.ForEach(it =>
  279. {
  280. var mappingInfo = primaryKey.SingleOrDefault(i => it.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase));
  281. if (mappingInfo != null && mappingInfo.Any())
  282. {
  283. it.IsPrimarykey = true;
  284. }
  285. });
  286. }
  287. if (this.UpdateBuilder.Parameters.HasValue() && this.UpdateBuilder.SetValues.IsValuable())
  288. {
  289. this.UpdateBuilder.Parameters.RemoveAll(it => this.UpdateBuilder.SetValues.Any(v => (SqlBuilder.SqlParameterKeyWord + SqlBuilder.GetNoTranslationColumnName(v.Key)) == it.ParameterName));
  290. }
  291. }
  292. private string GetDbColumnName(string propertyName)
  293. {
  294. if (!IsMappingColumns)
  295. {
  296. return propertyName;
  297. }
  298. if (this.Context.MappingColumns.Any(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase)))
  299. {
  300. this.MappingColumnList = this.Context.MappingColumns.Where(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase)).ToList();
  301. }
  302. if (MappingColumnList == null || !MappingColumnList.Any())
  303. {
  304. return propertyName;
  305. }
  306. else
  307. {
  308. var mappInfo = this.MappingColumnList.FirstOrDefault(it => it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase));
  309. return mappInfo == null ? propertyName : mappInfo.DbColumnName;
  310. }
  311. }
  312. private List<string> GetPrimaryKeys()
  313. {
  314. if (this.WhereColumnList.HasValue())
  315. {
  316. return this.WhereColumnList;
  317. }
  318. if (this.Context.IsSystemTablesConfig)
  319. {
  320. return this.Context.DbMaintenance.GetPrimaries(this.Context.EntityMaintenance.GetTableName(this.EntityInfo.EntityName));
  321. }
  322. else
  323. {
  324. return this.EntityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList();
  325. }
  326. }
  327. protected virtual List<string> GetIdentityKeys()
  328. {
  329. if (this.Context.IsSystemTablesConfig)
  330. {
  331. return this.Context.DbMaintenance.GetIsIdentities(this.Context.EntityMaintenance.GetTableName(this.EntityInfo.EntityName));
  332. }
  333. else
  334. {
  335. return this.EntityInfo.Columns.Where(it => it.IsIdentity).Select(it => it.DbColumnName).ToList();
  336. }
  337. }
  338. private void RestoreMapping()
  339. {
  340. if (IsAs)
  341. {
  342. this.Context.MappingTables = OldMappingTableList;
  343. }
  344. }
  345. private void TaskStart<Type>(Task<Type> result)
  346. {
  347. if (this.Context.CurrentConnectionConfig.IsShardSameThread)
  348. {
  349. Check.Exception(true, "IsShardSameThread=true can't be used async method");
  350. }
  351. result.Start();
  352. }
  353. private IUpdateable<T> CopyUpdateable()
  354. {
  355. var asyncContext = this.Context.Utilities.CopyContext(true);
  356. asyncContext.CurrentConnectionConfig.IsAutoCloseConnection = true;
  357. var asyncUpdateable = asyncContext.Updateable<T>(this.UpdateObjs);
  358. var asyncUpdateableBuilder = asyncUpdateable.UpdateBuilder;
  359. asyncUpdateableBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList;
  360. asyncUpdateableBuilder.IsNoUpdateNull = this.UpdateBuilder.IsNoUpdateNull;
  361. asyncUpdateableBuilder.Parameters = this.UpdateBuilder.Parameters;
  362. asyncUpdateableBuilder.sql = this.UpdateBuilder.sql;
  363. asyncUpdateableBuilder.WhereValues = this.UpdateBuilder.WhereValues;
  364. asyncUpdateableBuilder.TableWithString = this.UpdateBuilder.TableWithString;
  365. asyncUpdateableBuilder.TableName = this.UpdateBuilder.TableName;
  366. asyncUpdateableBuilder.PrimaryKeys = this.UpdateBuilder.PrimaryKeys;
  367. asyncUpdateableBuilder.IsOffIdentity = this.UpdateBuilder.IsOffIdentity;
  368. asyncUpdateableBuilder.SetValues = this.UpdateBuilder.SetValues;
  369. return asyncUpdateable;
  370. }
  371. }
  372. }