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.

360 lines
15 KiB

2 years ago
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Threading.Tasks;
  6. namespace SqlSugar
  7. {
  8. public class InsertableProvider<T> : IInsertable<T> where T : class, new()
  9. {
  10. public SqlSugarClient Context { get; set; }
  11. public IAdo Ado { get { return Context.Ado; } }
  12. public ISqlBuilder SqlBuilder { get; set; }
  13. public InsertBuilder InsertBuilder { get; set; }
  14. public bool IsMappingTable { get { return this.Context.MappingTables != null && this.Context.MappingTables.Any(); } }
  15. public bool IsMappingColumns { get { return this.Context.MappingColumns != null && this.Context.MappingColumns.Any(); } }
  16. public bool IsSingle { get { return this.InsertObjs.Length == 1; } }
  17. public EntityInfo EntityInfo { get; set; }
  18. public List<MappingColumn> MappingColumnList { get; set; }
  19. private List<string> IgnoreColumnNameList { get; set; }
  20. private bool IsOffIdentity { get; set; }
  21. public T[] InsertObjs { get; set; }
  22. public MappingTableList OldMappingTableList { get; set; }
  23. public bool IsAs { get; set; }
  24. #region Core
  25. public virtual int ExecuteCommand()
  26. {
  27. InsertBuilder.IsReturnIdentity = false;
  28. PreToSql();
  29. AutoRemoveDataCache();
  30. var sql = InsertBuilder.ToSqlString();
  31. RestoreMapping();
  32. return Ado.ExecuteCommand(sql, InsertBuilder.Parameters?.ToArray());
  33. }
  34. public virtual KeyValuePair<string, List<SugarParameter>> ToSql()
  35. {
  36. InsertBuilder.IsReturnIdentity = true;
  37. PreToSql();
  38. AutoRemoveDataCache();
  39. var sql = InsertBuilder.ToSqlString();
  40. RestoreMapping();
  41. return new KeyValuePair<string, List<SugarParameter>>(sql, InsertBuilder.Parameters);
  42. }
  43. public virtual int ExecuteReturnIdentity()
  44. {
  45. InsertBuilder.IsReturnIdentity = true;
  46. PreToSql();
  47. AutoRemoveDataCache();
  48. var sql = InsertBuilder.ToSqlString();
  49. RestoreMapping();
  50. return Ado.GetInt(sql, InsertBuilder.Parameters?.ToArray());
  51. }
  52. public virtual long ExecuteReturnBigIdentity()
  53. {
  54. InsertBuilder.IsReturnIdentity = true;
  55. PreToSql();
  56. AutoRemoveDataCache();
  57. var sql = InsertBuilder.ToSqlString();
  58. RestoreMapping();
  59. return Convert.ToInt64( Ado.GetScalar(sql, InsertBuilder.Parameters?.ToArray()));
  60. }
  61. public virtual T ExecuteReturnEntity()
  62. {
  63. ExecuteCommandIdentityIntoEntity();
  64. return InsertObjs.First();
  65. }
  66. public virtual bool ExecuteCommandIdentityIntoEntity()
  67. {
  68. var result = InsertObjs.First();
  69. var identityKeys = GetIdentityKeys();
  70. if (identityKeys.Count == 0) { return this.ExecuteCommand() > 0; }
  71. var idValue = ExecuteReturnBigIdentity();
  72. Check.Exception(identityKeys.Count > 1, "ExecuteCommandIdentityIntoEntity does not support multiple identity keys");
  73. var identityKey = identityKeys.First();
  74. object setValue= 0;
  75. setValue = idValue > int.MaxValue ? idValue : Convert.ToInt32(idValue);
  76. this.Context.EntityMaintenance.GetProperty<T>(identityKey).SetValue(result,setValue, null);
  77. return idValue>0;
  78. }
  79. public Task<int> ExecuteCommandAsync()
  80. {
  81. var result = new Task<int>(() =>
  82. {
  83. var asyncInsertable = CopyInsertable();
  84. return asyncInsertable.ExecuteCommand();
  85. });
  86. TaskStart(result);
  87. return result;
  88. }
  89. public Task<int> ExecuteReturnIdentityAsync()
  90. {
  91. var result = new Task<int>(() =>
  92. {
  93. var asyncInsertable = CopyInsertable();
  94. return asyncInsertable.ExecuteReturnIdentity();
  95. });
  96. TaskStart(result);
  97. return result;
  98. }
  99. public Task<T> ExecuteReturnEntityAsync()
  100. {
  101. var result = new Task<T>(() =>
  102. {
  103. var asyncInsertable = CopyInsertable();
  104. return asyncInsertable.ExecuteReturnEntity();
  105. });
  106. TaskStart(result);
  107. return result;
  108. }
  109. public Task<bool> ExecuteCommandIdentityIntoEntityAsync()
  110. {
  111. var result = new Task<bool>(() =>
  112. {
  113. var asyncInsertable = CopyInsertable();
  114. return asyncInsertable.ExecuteCommandIdentityIntoEntity();
  115. });
  116. TaskStart(result);
  117. return result;
  118. }
  119. public Task<long> ExecuteReturnBigIdentityAsync()
  120. {
  121. var result = new Task<long>(() =>
  122. {
  123. var asyncInsertable = CopyInsertable();
  124. return asyncInsertable.ExecuteReturnBigIdentity();
  125. });
  126. TaskStart(result);
  127. return result;
  128. }
  129. #endregion
  130. #region Setting
  131. public IInsertable<T> AS(string tableName)
  132. {
  133. var entityName = typeof(T).Name;
  134. IsAs = true;
  135. OldMappingTableList = this.Context.MappingTables;
  136. this.Context.MappingTables = this.Context.Utilities.TranslateCopy(this.Context.MappingTables);
  137. this.Context.MappingTables.Add(entityName, tableName);
  138. return this; ;
  139. }
  140. public IInsertable<T> IgnoreColumns(Expression<Func<T, object>> columns)
  141. {
  142. var ignoreColumns = InsertBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList();
  143. this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => !ignoreColumns.Any(ig => ig.Equals(it.PropertyName, StringComparison.CurrentCultureIgnoreCase))).ToList();
  144. return this;
  145. }
  146. public IInsertable<T> IgnoreColumns(Func<string, bool> ignoreColumMethod)
  147. {
  148. this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => !ignoreColumMethod?.Invoke(it.PropertyName) ?? default(bool)).ToList();
  149. return this;
  150. }
  151. public IInsertable<T> InsertColumns(Expression<Func<T, object>> columns)
  152. {
  153. var ignoreColumns = InsertBuilder.GetExpressionValue(columns, ResolveExpressType.ArraySingle).GetResultArray().Select(it => this.SqlBuilder.GetNoTranslationColumnName(it)).ToList();
  154. this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => ignoreColumns.Any(ig=>ig.Equals(it.PropertyName,StringComparison.CurrentCultureIgnoreCase))).ToList();
  155. return this;
  156. }
  157. public IInsertable<T> InsertColumns(Func<string, bool> insertColumMethod)
  158. {
  159. this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it => insertColumMethod?.Invoke(it.PropertyName) ?? default(bool)).ToList();
  160. return this;
  161. }
  162. public IInsertable<T> With(string lockString)
  163. {
  164. if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer)
  165. this.InsertBuilder.TableWithString = lockString;
  166. return this;
  167. }
  168. public IInsertable<T> Where(bool isNoInsertNull, bool isOffIdentity = false)
  169. {
  170. this.IsOffIdentity = isOffIdentity;
  171. if (this.InsertBuilder.LambdaExpressions == null)
  172. this.InsertBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.Context.CurrentConnectionConfig);
  173. this.InsertBuilder.IsNoInsertNull = isNoInsertNull;
  174. return this;
  175. }
  176. public IInsertable<T> RemoveDataCache()
  177. {
  178. var cacheService = this.Context.CurrentConnectionConfig.ConfigureExternalServices.DataInfoCacheService;
  179. CacheSchemeMain.RemoveCache(cacheService, this.Context.EntityMaintenance.GetTableName<T>());
  180. return this;
  181. }
  182. #endregion
  183. #region Protected Methods
  184. private void AutoRemoveDataCache()
  185. {
  186. var moreSetts = this.Context.CurrentConnectionConfig.MoreSettings;
  187. var extService = this.Context.CurrentConnectionConfig.ConfigureExternalServices;
  188. if (moreSetts != null && moreSetts.IsAutoRemoveDataCache && extService != null && extService.DataInfoCacheService != null)
  189. {
  190. this.RemoveDataCache();
  191. }
  192. }
  193. protected virtual void PreToSql()
  194. {
  195. #region Identities
  196. if (!IsOffIdentity)
  197. {
  198. var identities = GetIdentityKeys();
  199. if (identities != null && identities.Any())
  200. {
  201. this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it =>
  202. {
  203. return !identities.Any(i => it.DbColumnName.Equals(i, StringComparison.CurrentCultureIgnoreCase));
  204. }).ToList();
  205. }
  206. }
  207. #endregion
  208. #region IgnoreColumns
  209. if (this.Context.IgnoreColumns != null && this.Context.IgnoreColumns.Any())
  210. {
  211. var currentIgnoreColumns = this.Context.IgnoreColumns.Where(it => it.EntityName == this.EntityInfo.EntityName).ToList();
  212. this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it =>
  213. {
  214. return !currentIgnoreColumns.Any(i => it.PropertyName.Equals(i.PropertyName, StringComparison.CurrentCulture));
  215. }).ToList();
  216. }
  217. if (this.Context.IgnoreInsertColumns != null && this.Context.IgnoreInsertColumns.Any())
  218. {
  219. var currentIgnoreColumns = this.Context.IgnoreInsertColumns.Where(it => it.EntityName == this.EntityInfo.EntityName).ToList();
  220. this.InsertBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList.Where(it =>
  221. {
  222. return !currentIgnoreColumns.Any(i => it.PropertyName.Equals(i.PropertyName, StringComparison.CurrentCulture));
  223. }).ToList();
  224. }
  225. #endregion
  226. if (this.IsSingle)
  227. {
  228. foreach (var item in this.InsertBuilder.DbColumnInfoList)
  229. {
  230. if (this.InsertBuilder.Parameters == null) this.InsertBuilder.Parameters = new List<SugarParameter>();
  231. var paramters = new SugarParameter(this.SqlBuilder.SqlParameterKeyWord + item.DbColumnName, item.Value, item.PropertyType);
  232. if (InsertBuilder.IsNoInsertNull && paramters.Value == null)
  233. {
  234. continue;
  235. }
  236. this.InsertBuilder.Parameters.Add(paramters);
  237. }
  238. }
  239. }
  240. internal void Init()
  241. {
  242. InsertBuilder.EntityInfo = this.EntityInfo;
  243. Check.Exception(InsertObjs == null || InsertObjs.Count() == 0, "InsertObjs is null");
  244. var i = 0;
  245. foreach (var item in InsertObjs)
  246. {
  247. var insertItem = new List<DbColumnInfo>();
  248. foreach (var column in EntityInfo.Columns)
  249. {
  250. var columnInfo = new DbColumnInfo
  251. {
  252. Value = column.PropertyInfo.GetValue(item, null),
  253. DbColumnName = GetDbColumnName(column.PropertyName),
  254. PropertyName = column.PropertyName,
  255. PropertyType = UtilMethods.GetUnderType(column.PropertyInfo),
  256. TableId = i
  257. };
  258. if (columnInfo.PropertyType.IsEnum())
  259. {
  260. columnInfo.Value = Convert.ToInt64(columnInfo.Value);
  261. }
  262. insertItem.Add(columnInfo);
  263. }
  264. this.InsertBuilder.DbColumnInfoList.AddRange(insertItem);
  265. ++i;
  266. }
  267. }
  268. private string GetDbColumnName(string propertyName)
  269. {
  270. if (!IsMappingColumns)
  271. {
  272. return propertyName;
  273. }
  274. if (this.Context.MappingColumns.Any(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase)))
  275. {
  276. this.MappingColumnList = this.Context.MappingColumns.Where(it => it.EntityName.Equals(EntityInfo.EntityName, StringComparison.CurrentCultureIgnoreCase)).ToList();
  277. }
  278. if (MappingColumnList == null || !MappingColumnList.Any())
  279. {
  280. return propertyName;
  281. }
  282. else
  283. {
  284. var mappInfo = this.MappingColumnList.FirstOrDefault(it => it.PropertyName.Equals(propertyName, StringComparison.CurrentCultureIgnoreCase));
  285. return mappInfo == null ? propertyName : mappInfo.DbColumnName;
  286. }
  287. }
  288. protected virtual List<string> GetPrimaryKeys()
  289. {
  290. if (this.Context.IsSystemTablesConfig)
  291. {
  292. return this.Context.DbMaintenance.GetPrimaries(this.Context.EntityMaintenance.GetTableName(this.EntityInfo.EntityName));
  293. }
  294. else
  295. {
  296. return this.EntityInfo.Columns.Where(it => it.IsPrimarykey).Select(it => it.DbColumnName).ToList();
  297. }
  298. }
  299. protected virtual List<string> GetIdentityKeys()
  300. {
  301. if (this.Context.IsSystemTablesConfig)
  302. {
  303. return this.Context.DbMaintenance.GetIsIdentities(this.Context.EntityMaintenance.GetTableName(this.EntityInfo.EntityName));
  304. }
  305. else
  306. {
  307. return this.EntityInfo.Columns.Where(it => it.IsIdentity).Select(it => it.DbColumnName).ToList();
  308. }
  309. }
  310. private void TaskStart<Type>(Task<Type> result)
  311. {
  312. if (this.Context.CurrentConnectionConfig.IsShardSameThread)
  313. {
  314. Check.Exception(true, "IsShardSameThread=true can't be used async method");
  315. }
  316. result.Start();
  317. }
  318. protected void RestoreMapping()
  319. {
  320. if (IsAs)
  321. {
  322. this.Context.MappingTables = OldMappingTableList;
  323. }
  324. }
  325. protected IInsertable<T> CopyInsertable()
  326. {
  327. var asyncContext = this.Context.Utilities.CopyContext(true);
  328. asyncContext.CurrentConnectionConfig.IsAutoCloseConnection = true;
  329. var asyncInsertable = asyncContext.Insertable<T>(this.InsertObjs);
  330. var asyncInsertableBuilder = asyncInsertable.InsertBuilder;
  331. asyncInsertableBuilder.DbColumnInfoList = this.InsertBuilder.DbColumnInfoList;
  332. asyncInsertableBuilder.EntityInfo = this.InsertBuilder.EntityInfo;
  333. asyncInsertableBuilder.Parameters = this.InsertBuilder.Parameters;
  334. asyncInsertableBuilder.sql = this.InsertBuilder.sql;
  335. asyncInsertableBuilder.IsNoInsertNull = this.InsertBuilder.IsNoInsertNull;
  336. asyncInsertableBuilder.IsReturnIdentity = this.InsertBuilder.IsReturnIdentity;
  337. asyncInsertableBuilder.EntityInfo = this.InsertBuilder.EntityInfo;
  338. asyncInsertableBuilder.TableWithString = this.InsertBuilder.TableWithString;
  339. return asyncInsertable;
  340. }
  341. #endregion
  342. }
  343. }