csharp/2881099/FreeSql.DbContext/FreeSql.DbContext/DbSet/DbSetAsync.cs

DbSetAsync.cs
using FreeSql.Extensions.EnsatyUtil;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace FreeSql {
	partial clast DbSet {

		Task DbContextExecCommandAsync() {
			_dicUpdateTimes.Clear();
			return _ctx.ExecCommandAsync();
		}

		async Task DbContextBetchAddAsync(EnsatyState[] adds) {
			if (adds.Any() == false) return 0;
			var affrows = await this.OrmInsert(adds.Select(a => a.Value)).ExecuteAffrowsAsync();
			return affrows;
		}

		#region Add
		async Task AddPrivAsync(TEnsaty data, bool isCheck) {
			if (isCheck && CanAdd(data, true) == false) return;
			if (_tableIdensatys.Length > 0) {
				//有自增,马上执行
				switch (_fsql.Ado.DataType) {
					case DataType.SqlServer:
					case DataType.PostgreSQL:
						if (_tableIdensatys.Length == 1 && _table.Primarys.Length == 1) {
							await DbContextExecCommandAsync();
							var idtval = await this.OrmInsert(data).ExecuteIdensatyAsync();
							IncrAffrows(1);
							_fsql.SetEnsatyIdensatyValueWithPrimary(_ensatyType, data, idtval);
							Attach(data);
							if (_ctx.Options.EnableAddOrUpdateNavigateList)
								await AddOrUpdateNavigateListAsync(data);
						} else {
							await DbContextExecCommandAsync();
							var newval = (await this.OrmInsert(data).ExecuteInsertedAsync()).First();
							IncrAffrows(1);
							_fsql.MapEnsatyValue(_ensatyType, newval, data);
							Attach(newval);
							if (_ctx.Options.EnableAddOrUpdateNavigateList)
								await AddOrUpdateNavigateListAsync(data);
						}
						return;
					case DataType.MySql:
					case DataType.Oracle:
					case DataType.Sqlite:
						if (_tableIdensatys.Length == 1 && _table.Primarys.Length == 1) {
							await DbContextExecCommandAsync();
							var idtval = await this.OrmInsert(data).ExecuteIdensatyAsync();
							IncrAffrows(1);
							_fsql.SetEnsatyIdensatyValueWithPrimary(_ensatyType, data, idtval);
							Attach(data);
							if (_ctx.Options.EnableAddOrUpdateNavigateList)
								await AddOrUpdateNavigateListAsync(data);
						}
						return;
				}
			}
			EnqueueToDbContext(DbContext.ExecCommandInfoType.Insert, CreateEnsatyState(data));
			Attach(data);
			if (_ctx.Options.EnableAddOrUpdateNavigateList)
				await AddOrUpdateNavigateListAsync(data);
		}
		public Task AddAsync(TEnsaty data) => AddPrivAsync(data, true);
		async public Task AddRangeAsync(IEnumerable data) {
			if (CanAdd(data, true) == false) return;
			if (data.ElementAtOrDefault(1) == default(TEnsaty)) {
				await AddAsync(data.First());
				return;
			}
			if (_tableIdensatys.Length > 0) {
				//有自增,马上执行
				switch (_fsql.Ado.DataType) {
					case DataType.SqlServer:
					case DataType.PostgreSQL:
						await DbContextExecCommandAsync();
						var rets = await this.OrmInsert(data).ExecuteInsertedAsync();
						if (rets.Count != data.Count()) throw new Exception($"特别错误:批量添加失败,{_fsql.Ado.DataType} 的返回数据,与添加的数目不匹配");
						var idx = 0;
						foreach (var s in data)
							_fsql.MapEnsatyValue(_ensatyType, rets[idx++], s);
						IncrAffrows(rets.Count);
						AttachRange(rets);
						if (_ctx.Options.EnableAddOrUpdateNavigateList)
							foreach (var item in data)
								await AddOrUpdateNavigateListAsync(item);
						return;
					case DataType.MySql:
					case DataType.Oracle:
					case DataType.Sqlite:
						foreach (var s in data)
							await AddPrivAsync(s, false);
						return;
				}
			} else {
				//进入队列,等待 SaveChanges 时执行
				foreach (var item in data)
					EnqueueToDbContext(DbContext.ExecCommandInfoType.Insert, CreateEnsatyState(item));
				AttachRange(data);
				if (_ctx.Options.EnableAddOrUpdateNavigateList)
					foreach (var item in data)
						await AddOrUpdateNavigateListAsync(item);
			}
		}
		async Task AddOrUpdateNavigateListAsync(TEnsaty item) {
			Type itemType = null;
			foreach (var prop in _table.Properties) {
				if (_table.ColumnsByCsIgnore.ContainsKey(prop.Key)) continue;
				if (_table.ColumnsByCs.ContainsKey(prop.Key)) continue;
				var tref = _table.GetTableRef(prop.Key, true);
				if (tref == null) continue;

				switch (tref.RefType) {
					case Internal.Model.TableRefType.OneToOne:
					case Internal.Model.TableRefType.ManyToOne:
					case Internal.Model.TableRefType.ManyToMany:
						continue;
					case Internal.Model.TableRefType.OneToMany:
						if (itemType == null) itemType = item.GetType();
						if (_table.TypeLazy != null && itemType == _table.TypeLazy) {
							var lazyField = _dicLazyIsSetField.GetOrAdd(_table.TypeLazy, tl => new ConcurrentDictionary()).GetOrAdd(prop.Key, propName =>
								_table.TypeLazy.GetField($"__lazy__{propName}", System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance));
							if (lazyField != null) {
								var lazyFieldValue = (bool)lazyField.GetValue(item);
								if (lazyFieldValue == false) continue;
							}
						}
						var propVal = prop.Value.GetValue(item);
						var propValEach = propVal as IEnumerable;
						if (propValEach == null) continue;
						object dbset = null;
						System.Reflection.MethodInfo dbsetAddOrUpdate = null;
						foreach (var propValItem in propValEach) {
							if (dbset == null) {
								dbset = _ctx.Set(tref.RefEnsatyType);
								dbsetAddOrUpdate = dbset.GetType().GetMethod("AddOrUpdateAsync", new Type[] { tref.RefEnsatyType });
							}
							for (var colidx = 0; colidx < tref.Columns.Count; colidx++) {
								tref.RefColumns[colidx].Table.Properties[tref.RefColumns[colidx].CsName]
									.SetValue(propValItem, tref.Columns[colidx].Table.Properties[tref.Columns[colidx].CsName].GetValue(item));
							}
							Task task = dbsetAddOrUpdate.Invoke(dbset, new object[] { propValItem }) as Task;
							await task;
						}
						break;
				}
			}
		}
		#endregion

		#region UpdateAsync
		Task DbContextBetchUpdateAsync(EnsatyState[] ups) => DbContextBetchUpdatePrivAsync(ups, false);
		Task DbContextBetchUpdateNowAsync(EnsatyState[] ups) => DbContextBetchUpdatePrivAsync(ups, true);
		async Task DbContextBetchUpdatePrivAsync(EnsatyState[] ups, bool isLiveUpdate) {
			if (ups.Any() == false) return 0;
			var uplst1 = ups[ups.Length - 1];
			var uplst2 = ups.Length > 1 ? ups[ups.Length - 2] : null;

			if (_states.TryGetValue(uplst1.Key, out var lstval1) == false) return -999;
			var lstval2 = default(EnsatyState);
			if (uplst2 != null && _states.TryGetValue(uplst2.Key, out lstval2) == false) throw new Exception($"特别错误:更新失败,数据未被跟踪:{_fsql.GetEnsatyString(_ensatyType, uplst2.Value)}");

			var cuig1 = _fsql.CompareEnsatyValueReturnColumns(_ensatyType, uplst1.Value, lstval1.Value, true);
			var cuig2 = uplst2 != null ? _fsql.CompareEnsatyValueReturnColumns(_ensatyType, uplst2.Value, lstval2.Value, true) : null;

			List data = null;
			string[] cuig = null;
			if (uplst2 != null && string.Compare(string.Join(",", cuig1), string.Join(",", cuig2)) != 0) {
				//最后一个不保存
				data = ups.ToList();
				data.RemoveAt(ups.Length - 1);
				cuig = cuig2;
			} else if (isLiveUpdate) {
				//立即保存
				data = ups.ToList();
				cuig = cuig1;
			}

			if (data?.Count > 0) {

				if (cuig.Length == _table.Columns.Count)
					return ups.Length == data.Count ? -998 : -997;

				var updateSource = data.Select(a => a.Value).ToArray();
				var update = this.OrmUpdate(null).SetSource(updateSource).IgnoreColumns(cuig);

				var affrows = await update.ExecuteAffrowsAsync();

				foreach (var newval in data) {
					if (_states.TryGetValue(newval.Key, out var tryold))
						_fsql.MapEnsatyValue(_ensatyType, newval.Value, tryold.Value);
					if (newval.OldValue != null)
						_fsql.MapEnsatyValue(_ensatyType, newval.Value, newval.OldValue);
				}
				return affrows;
			}

			//等待下次对比再保存
			return 0;
		}
		async public Task UpdateAsync(TEnsaty data) {
			var exists = ExistsInStates(data);
			if (exists == null) throw new Exception($"不可更新,未设置主键的值:{_fsql.GetEnsatyString(_ensatyType, data)}");
			if (exists == false) {
				var olddata = await OrmSelect(data).FirstAsync();
				if (olddata == null) throw new Exception($"不可更新,数据库不存在该记录:{_fsql.GetEnsatyString(_ensatyType, data)}");
			}

			await UpdateRangePrivAsync(new[] { data }, true);
		}
		public Task UpdateRangeAsync(IEnumerable data) => UpdateRangePrivAsync(data, true);
		async Task UpdateRangePrivAsync(IEnumerable data, bool isCheck) {
			if (CanUpdate(data, true) == false) return;
			foreach (var item in data) {
				if (_dicUpdateTimes.ContainsKey(item))
					await DbContextExecCommandAsync();
				_dicUpdateTimes.Add(item, 1);

				var state = CreateEnsatyState(item);
				state.OldValue = item;
				EnqueueToDbContext(DbContext.ExecCommandInfoType.Update, state);
			}
			if (_ctx.Options.EnableAddOrUpdateNavigateList)
				foreach (var item in data)
					await AddOrUpdateNavigateListAsync(item);
		}
		#endregion

		#region RemoveAsync
		async Task DbContextBetchRemoveAsync(EnsatyState[] dels) {
			if (dels.Any() == false) return 0;
			var affrows = await this.OrmDelete(dels.Select(a => a.Value)).ExecuteAffrowsAsync();
			return Math.Max(dels.Length, affrows);
		}
		#endregion

		#region AddOrUpdateAsync
		async public Task AddOrUpdateAsync(TEnsaty data) {
			if (data == null) throw new ArgumentNullException(nameof(data));
			if (_table.Primarys.Any() == false) throw new Exception($"不可添加,实体没有主键:{_fsql.GetEnsatyString(_ensatyType, data)}");

			var flagExists = ExistsInStates(data);
			if (flagExists == false) {
				var olddata = await OrmSelect(data).FirstAsync();
				if (olddata == null) flagExists = false;
			}

			if (flagExists == true && CanUpdate(data, false)) {
				await DbContextExecCommandAsync();
				var affrows = _ctx._affrows;
				await UpdateRangePrivAsync(new[] { data }, false);
				await DbContextExecCommandAsync();
				affrows = _ctx._affrows - affrows;
				if (affrows > 0) return;
			}
			if (CanAdd(data, false)) {
				_fsql.ClearEnsatyPrimaryValueWithIdensaty(_ensatyType, data);
				await AddPrivAsync(data, false);
			}
		}
		#endregion
	}
}