mirror of
				https://github.com/nsnail/FreeSql.git
				synced 2025-11-04 09:15:27 +08:00 
			
		
		
		
	## v0.3.21
- 增加 IUpdate IgnoreColumns 重载方法,支持传入字符串数组忽略修改; - 完善 FreeSql.DbContext,支持对象操作 + SaveChanges 最后保存操作;
This commit is contained in:
		@@ -28,18 +28,69 @@ namespace dbcontext_01.Controllers
 | 
				
			|||||||
			try {
 | 
								try {
 | 
				
			||||||
				using (var ctx = new SongContext()) {
 | 
									using (var ctx = new SongContext()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					id = await ctx.Songs.Insert(new Song { }).ExecuteIdentityAsync();
 | 
										var song = new Song { };
 | 
				
			||||||
 | 
										ctx.Songs.Add(song);
 | 
				
			||||||
 | 
										id = song.Id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					var item = await ctx.Songs.Select.Where(a => a.Id == id).FirstAsync();
 | 
										var adds = Enumerable.Range(0, 100)
 | 
				
			||||||
 | 
											.Select(a => new Song { Create_time = DateTime.Now, Is_deleted = false, Title = "xxxx" + a, Url = "url222" })
 | 
				
			||||||
 | 
											.ToList();
 | 
				
			||||||
 | 
										ctx.Songs.AddRange(adds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
					throw new Exception("回滚");
 | 
										for (var a = 0; a < adds.Count; a++)
 | 
				
			||||||
 | 
											adds[a].Title = "dkdkdkdk" + a;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										ctx.Songs.UpdateRange(adds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										ctx.Songs.RemoveRange(adds.Skip(10).Take(20).ToList());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										//ctx.Songs.Update(adds.First());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										adds.Last().Url = "skldfjlksdjglkjjcccc";
 | 
				
			||||||
 | 
										ctx.Songs.Update(adds.Last());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										//throw new Exception("回滚");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										ctx.SaveChanges();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									using (var ctx = new SongContext()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										var song = new Song { };
 | 
				
			||||||
 | 
										await ctx.Songs.AddAsync(song);
 | 
				
			||||||
 | 
										id = song.Id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										var adds = Enumerable.Range(0, 100)
 | 
				
			||||||
 | 
											.Select(a => new Song { Create_time = DateTime.Now, Is_deleted = false, Title = "xxxx" + a, Url = "url222" })
 | 
				
			||||||
 | 
											.ToList();
 | 
				
			||||||
 | 
										await ctx.Songs.AddRangeAsync(adds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										for (var a = 0; a < adds.Count; a++)
 | 
				
			||||||
 | 
											adds[a].Title = "dkdkdkdk" + a;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										ctx.Songs.UpdateRange(adds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										ctx.Songs.RemoveRange(adds.Skip(10).Take(20).ToList());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										//ctx.Songs.Update(adds.First());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										adds.Last().Url = "skldfjlksdjglkjjcccc";
 | 
				
			||||||
 | 
										ctx.Songs.Update(adds.Last());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										//throw new Exception("回滚");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										await ctx.SaveChangesAsync();
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} catch {
 | 
								} catch {
 | 
				
			||||||
				var item = await _orm.Select<Song>().Where(a => a.Id == id).FirstAsync();
 | 
									var item = await _orm.Select<Song>().Where(a => a.Id == id).FirstAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				throw;
 | 
									throw;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var item22 = await _orm.Select<Song>().Where(a => a.Id == id).FirstAsync();
 | 
				
			||||||
 | 
								var item33 = await _orm.Select<Song>().Where(a => a.Id > id).ToListAsync();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return item22.Title;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // GET api/values/5
 | 
					        // GET api/values/5
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,6 +41,12 @@ namespace orm_vs
 | 
				
			|||||||
				//optionsBuilder.UseMySql("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=21;Max Pool Size=21");
 | 
									//optionsBuilder.UseMySql("Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;Initial Catalog=cccddd;Charset=utf8;SslMode=none;Min Pool Size=21;Max Pool Size=21");
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							class FreeSongContext: FreeSql.DbContext {
 | 
				
			||||||
 | 
								public FreeSql.DbSet<Song> Songs { get; set; }
 | 
				
			||||||
 | 
								protected override void OnConfiguring(FreeSql.DbContextOptionsBuilder builder) {
 | 
				
			||||||
 | 
									builder.UseFreeSql(fsql);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		static void Main(string[] args) {
 | 
							static void Main(string[] args) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -143,8 +149,14 @@ namespace orm_vs
 | 
				
			|||||||
			Stopwatch sw = new Stopwatch();
 | 
								Stopwatch sw = new Stopwatch();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			sw.Restart();
 | 
								sw.Restart();
 | 
				
			||||||
			for (var a = 0; a < forTime; a++)
 | 
								for (var a = 0; a < forTime; a++) {
 | 
				
			||||||
				fsql.Insert(songs).ExecuteAffrows();
 | 
									//fsql.Insert(songs).ExecuteAffrows();
 | 
				
			||||||
 | 
									using (var db = new FreeSongContext()) {
 | 
				
			||||||
 | 
										//db.Configuration.AutoDetectChangesEnabled = false;
 | 
				
			||||||
 | 
										db.Songs.AddRange(songs.ToArray());
 | 
				
			||||||
 | 
										db.SaveChanges();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			sw.Stop();
 | 
								sw.Stop();
 | 
				
			||||||
			sb.AppendLine($"FreeSql Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
 | 
								sb.AppendLine($"FreeSql Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,6 +12,7 @@
 | 
				
			|||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <ItemGroup>
 | 
					  <ItemGroup>
 | 
				
			||||||
 | 
					    <ProjectReference Include="..\..\FreeSql.DbContext\FreeSql.DbContext.csproj" />
 | 
				
			||||||
    <ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
 | 
					    <ProjectReference Include="..\..\FreeSql\FreeSql.csproj" />
 | 
				
			||||||
  </ItemGroup>
 | 
					  </ItemGroup>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,9 +5,10 @@ using System.Collections.Concurrent;
 | 
				
			|||||||
using System.Data.Common;
 | 
					using System.Data.Common;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Reflection;
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					using System.Linq.Expressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FreeSql {
 | 
					namespace FreeSql {
 | 
				
			||||||
	public abstract class DbContext : IDisposable {
 | 
						public abstract partial class DbContext : IDisposable {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		internal IFreeSql _orm;
 | 
							internal IFreeSql _orm;
 | 
				
			||||||
		internal IFreeSql _fsql => _orm ?? throw new ArgumentNullException("请在 OnConfiguring 或 AddFreeDbContext 中配置 UseFreeSql");
 | 
							internal IFreeSql _fsql => _orm ?? throw new ArgumentNullException("请在 OnConfiguring 或 AddFreeDbContext 中配置 UseFreeSql");
 | 
				
			||||||
@@ -44,8 +45,143 @@ namespace FreeSql {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		protected Dictionary<PropertyInfo, object> AllSets => new Dictionary<PropertyInfo, object>();
 | 
							protected Dictionary<PropertyInfo, object> AllSets => new Dictionary<PropertyInfo, object>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public void SaveChanges() {
 | 
							public long SaveChanges() {
 | 
				
			||||||
 | 
								ExecCommand();
 | 
				
			||||||
			Commit();
 | 
								Commit();
 | 
				
			||||||
 | 
								return _affrows;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							internal class ExecCommandInfo {
 | 
				
			||||||
 | 
								public ExecCommandInfoType actionType { get; set; }
 | 
				
			||||||
 | 
								public Type entityType { get; set; }
 | 
				
			||||||
 | 
								public object dbSet { get; set; }
 | 
				
			||||||
 | 
								public object state { get; set; }
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							internal enum ExecCommandInfoType { Insert, Update, Delete }
 | 
				
			||||||
 | 
							Queue<ExecCommandInfo> _actions = new Queue<ExecCommandInfo>();
 | 
				
			||||||
 | 
							internal long _affrows = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							internal void EnqueueAction(ExecCommandInfoType actionType, Type entityType, object dbSet, object state) {
 | 
				
			||||||
 | 
								_actions.Enqueue(new ExecCommandInfo { actionType = actionType, entityType = entityType, dbSet = dbSet, state = state });
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							static ConcurrentDictionary<Type, Func<object, object[], int>> _dicExecCommandInsert = new ConcurrentDictionary<Type, Func<object, object[], int>>();
 | 
				
			||||||
 | 
							static ConcurrentDictionary<Type, Func<object, object[], int>> _dicExecCommandDelete = new ConcurrentDictionary<Type, Func<object, object[], int>>();
 | 
				
			||||||
 | 
							static ConcurrentDictionary<Type, Func<object, object[], bool, int>> _dicExecCommandUpdate = new ConcurrentDictionary<Type, Func<object, object[], bool, int>>();
 | 
				
			||||||
 | 
							internal void ExecCommand() {
 | 
				
			||||||
 | 
								ExecCommandInfo oldinfo = null;
 | 
				
			||||||
 | 
								var states = new List<object>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								Action funcInsert = () => {
 | 
				
			||||||
 | 
									var insertFunc = _dicExecCommandInsert.GetOrAdd(oldinfo.entityType, t => {
 | 
				
			||||||
 | 
										var arrType = t.MakeArrayType();
 | 
				
			||||||
 | 
										var dbsetType = typeof(DbSet<>).MakeGenericType(t);
 | 
				
			||||||
 | 
										var dbsetTypeInsert = dbsetType.GetMethod("OrmInsert", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType }, null);
 | 
				
			||||||
 | 
										var insertBuilder = typeof(IInsert<>).MakeGenericType(t);
 | 
				
			||||||
 | 
										var insertExecuteAffrows = insertBuilder.GetMethod("ExecuteAffrows", new Type[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										var returnTarget = Expression.Label(typeof(int));
 | 
				
			||||||
 | 
										var parm1DbSet = Expression.Parameter(typeof(object));
 | 
				
			||||||
 | 
										var parm2Vals = Expression.Parameter(typeof(object[]));
 | 
				
			||||||
 | 
										var var1Vals = Expression.Variable(arrType);
 | 
				
			||||||
 | 
										return Expression.Lambda<Func<object, object[], int>>(Expression.Block(
 | 
				
			||||||
 | 
											new[] { var1Vals },
 | 
				
			||||||
 | 
											Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)),
 | 
				
			||||||
 | 
											Expression.Return(returnTarget,
 | 
				
			||||||
 | 
												Expression.Call(
 | 
				
			||||||
 | 
													Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeInsert, var1Vals),
 | 
				
			||||||
 | 
													insertExecuteAffrows
 | 
				
			||||||
 | 
												)
 | 
				
			||||||
 | 
											),
 | 
				
			||||||
 | 
											Expression.Label(returnTarget, Expression.Default(typeof(int)))
 | 
				
			||||||
 | 
										), new[] { parm1DbSet, parm2Vals }).Compile();
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									_affrows += insertFunc(oldinfo.dbSet, states.ToArray());
 | 
				
			||||||
 | 
									states.Clear();
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								Action funcDelete = () => {
 | 
				
			||||||
 | 
									var deleteFunc = _dicExecCommandDelete.GetOrAdd(oldinfo.entityType, t => {
 | 
				
			||||||
 | 
										var arrType = t.MakeArrayType();
 | 
				
			||||||
 | 
										var dbsetType = typeof(DbSet<>).MakeGenericType(t);
 | 
				
			||||||
 | 
										var dbsetTypeDelete = dbsetType.GetMethod("DbContextBetchRemove", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType }, null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										var returnTarget = Expression.Label(typeof(int));
 | 
				
			||||||
 | 
										var parm1DbSet = Expression.Parameter(typeof(object));
 | 
				
			||||||
 | 
										var parm2Vals = Expression.Parameter(typeof(object[]));
 | 
				
			||||||
 | 
										var var1Vals = Expression.Variable(arrType);
 | 
				
			||||||
 | 
										return Expression.Lambda<Func<object, object[], int>>(Expression.Block(
 | 
				
			||||||
 | 
											new[] { var1Vals },
 | 
				
			||||||
 | 
											Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)),
 | 
				
			||||||
 | 
											Expression.Return(returnTarget, Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeDelete, var1Vals)),
 | 
				
			||||||
 | 
											Expression.Label(returnTarget, Expression.Default(typeof(int)))
 | 
				
			||||||
 | 
										), new[] { parm1DbSet, parm2Vals }).Compile();
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									_affrows += deleteFunc(oldinfo.dbSet, states.ToArray());
 | 
				
			||||||
 | 
									states.Clear();
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								Action<bool> funcUpdate = isLiveUpdate => {
 | 
				
			||||||
 | 
									var updateFunc = _dicExecCommandUpdate.GetOrAdd(oldinfo.entityType, t => {
 | 
				
			||||||
 | 
										var arrType = t.MakeArrayType();
 | 
				
			||||||
 | 
										var dbsetType = typeof(DbSet<>).MakeGenericType(t);
 | 
				
			||||||
 | 
										var dbsetTypeUpdate = dbsetType.GetMethod("DbContextBetchUpdate", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType, typeof(bool) }, null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										var returnTarget = Expression.Label(typeof(int));
 | 
				
			||||||
 | 
										var parm1DbSet = Expression.Parameter(typeof(object));
 | 
				
			||||||
 | 
										var parm2Vals = Expression.Parameter(typeof(object[]));
 | 
				
			||||||
 | 
										var parm3IsLiveUpdate = Expression.Parameter(typeof(bool));
 | 
				
			||||||
 | 
										var var1Vals = Expression.Variable(arrType);
 | 
				
			||||||
 | 
										return Expression.Lambda<Func<object, object[], bool, int>>(Expression.Block(
 | 
				
			||||||
 | 
											new[] { var1Vals },
 | 
				
			||||||
 | 
											Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)),
 | 
				
			||||||
 | 
											Expression.Return(returnTarget, Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeUpdate, var1Vals, parm3IsLiveUpdate)),
 | 
				
			||||||
 | 
											Expression.Label(returnTarget, Expression.Default(typeof(int)))
 | 
				
			||||||
 | 
										), new[] { parm1DbSet, parm2Vals, parm3IsLiveUpdate }).Compile();
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									var affrows = updateFunc(oldinfo.dbSet, states.ToArray(), isLiveUpdate);
 | 
				
			||||||
 | 
									if (affrows > 0) {
 | 
				
			||||||
 | 
										_affrows += affrows;
 | 
				
			||||||
 | 
										var islastNotUpdated = states.Count != affrows;
 | 
				
			||||||
 | 
										states.Clear();
 | 
				
			||||||
 | 
										if (islastNotUpdated) states.Add(oldinfo.state);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								while(_actions.Any() || states.Any()) {
 | 
				
			||||||
 | 
									var info = _actions.Any() ? _actions.Dequeue() : null;
 | 
				
			||||||
 | 
									if (oldinfo == null) oldinfo = info;
 | 
				
			||||||
 | 
									var isLiveUpdate = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (_actions.Any() == false && states.Any() ||
 | 
				
			||||||
 | 
										info != null && oldinfo.actionType != info.actionType ||
 | 
				
			||||||
 | 
										info != null && oldinfo.entityType != info.entityType) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (info != null && oldinfo.actionType == info.actionType && oldinfo.entityType == info.entityType) {
 | 
				
			||||||
 | 
											//最后一个,合起来发送
 | 
				
			||||||
 | 
											states.Add(info.state);
 | 
				
			||||||
 | 
											info = null;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										switch (oldinfo.actionType) {
 | 
				
			||||||
 | 
											case ExecCommandInfoType.Insert:
 | 
				
			||||||
 | 
												funcInsert();
 | 
				
			||||||
 | 
												break;
 | 
				
			||||||
 | 
											case ExecCommandInfoType.Delete:
 | 
				
			||||||
 | 
												funcDelete();
 | 
				
			||||||
 | 
												break;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										isLiveUpdate = true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (isLiveUpdate || oldinfo.actionType == ExecCommandInfoType.Update) {
 | 
				
			||||||
 | 
										if (states.Any())
 | 
				
			||||||
 | 
											funcUpdate(isLiveUpdate);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (info != null) {
 | 
				
			||||||
 | 
										states.Add(info.state);
 | 
				
			||||||
 | 
										oldinfo = info;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		void ReturnObject() {
 | 
							void ReturnObject() {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										139
									
								
								FreeSql.DbContext/DbContextAsync.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								FreeSql.DbContext/DbContextAsync.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,139 @@
 | 
				
			|||||||
 | 
					using SafeObjectPool;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
 | 
					using System.Data.Common;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					using System.Linq.Expressions;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace FreeSql {
 | 
				
			||||||
 | 
						partial class DbContext {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							async public Task<long> SaveChangesAsync() {
 | 
				
			||||||
 | 
								await ExecCommandAsync();
 | 
				
			||||||
 | 
								Commit();
 | 
				
			||||||
 | 
								return _affrows;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							static ConcurrentDictionary<Type, Func<object, object[], Task<int>>> _dicExecCommandAsyncInsert = new ConcurrentDictionary<Type, Func<object, object[], Task<int>>>();
 | 
				
			||||||
 | 
							static ConcurrentDictionary<Type, Func<object, object[], Task<int>>> _dicExecCommandAsyncDelete = new ConcurrentDictionary<Type, Func<object, object[], Task<int>>>();
 | 
				
			||||||
 | 
							static ConcurrentDictionary<Type, Func<object, object[], bool, Task<int>>> _dicExecCommandAsyncUpdate = new ConcurrentDictionary<Type, Func<object, object[], bool, Task<int>>>();
 | 
				
			||||||
 | 
							async internal Task ExecCommandAsync() {
 | 
				
			||||||
 | 
								ExecCommandInfo oldinfo = null;
 | 
				
			||||||
 | 
								var states = new List<object>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								Func<Task> funcInsert = async () => {
 | 
				
			||||||
 | 
									var insertFunc = _dicExecCommandAsyncInsert.GetOrAdd(oldinfo.entityType, t => {
 | 
				
			||||||
 | 
										var arrType = t.MakeArrayType();
 | 
				
			||||||
 | 
										var dbsetType = typeof(DbSet<>).MakeGenericType(t);
 | 
				
			||||||
 | 
										var dbsetTypeInsert = dbsetType.GetMethod("OrmInsert", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType }, null);
 | 
				
			||||||
 | 
										var insertBuilder = typeof(IInsert<>).MakeGenericType(t);
 | 
				
			||||||
 | 
										var insertExecuteAffrows = insertBuilder.GetMethod("ExecuteAffrowsAsync", new Type[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										var returnTarget = Expression.Label(typeof(Task<int>));
 | 
				
			||||||
 | 
										var parm1DbSet = Expression.Parameter(typeof(object));
 | 
				
			||||||
 | 
										var parm2Vals = Expression.Parameter(typeof(object[]));
 | 
				
			||||||
 | 
										var var1Vals = Expression.Variable(arrType);
 | 
				
			||||||
 | 
										return Expression.Lambda<Func<object, object[], Task<int>>>(Expression.Block(
 | 
				
			||||||
 | 
											new[] { var1Vals },
 | 
				
			||||||
 | 
											Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)),
 | 
				
			||||||
 | 
											Expression.Return(returnTarget,
 | 
				
			||||||
 | 
												Expression.Call(
 | 
				
			||||||
 | 
													Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeInsert, var1Vals),
 | 
				
			||||||
 | 
													insertExecuteAffrows
 | 
				
			||||||
 | 
												)
 | 
				
			||||||
 | 
											),
 | 
				
			||||||
 | 
											Expression.Label(returnTarget, Expression.Default(typeof(Task<int>)))
 | 
				
			||||||
 | 
										), new[] { parm1DbSet, parm2Vals }).Compile();
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									_affrows += await insertFunc(oldinfo.dbSet, states.ToArray());
 | 
				
			||||||
 | 
									states.Clear();
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								Func<Task> funcDelete = async () => {
 | 
				
			||||||
 | 
									var deleteFunc = _dicExecCommandAsyncDelete.GetOrAdd(oldinfo.entityType, t => {
 | 
				
			||||||
 | 
										var arrType = t.MakeArrayType();
 | 
				
			||||||
 | 
										var dbsetType = typeof(DbSet<>).MakeGenericType(t);
 | 
				
			||||||
 | 
										var dbsetTypeDelete = dbsetType.GetMethod("DbContextBetchRemoveAsync", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType }, null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										var returnTarget = Expression.Label(typeof(Task<int>));
 | 
				
			||||||
 | 
										var parm1DbSet = Expression.Parameter(typeof(object));
 | 
				
			||||||
 | 
										var parm2Vals = Expression.Parameter(typeof(object[]));
 | 
				
			||||||
 | 
										var var1Vals = Expression.Variable(arrType);
 | 
				
			||||||
 | 
										return Expression.Lambda<Func<object, object[], Task<int>>>(Expression.Block(
 | 
				
			||||||
 | 
											new[] { var1Vals },
 | 
				
			||||||
 | 
											Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)),
 | 
				
			||||||
 | 
											Expression.Return(returnTarget, Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeDelete, var1Vals)),
 | 
				
			||||||
 | 
											Expression.Label(returnTarget, Expression.Default(typeof(Task<int>)))
 | 
				
			||||||
 | 
										), new[] { parm1DbSet, parm2Vals }).Compile();
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									_affrows += await deleteFunc(oldinfo.dbSet, states.ToArray());
 | 
				
			||||||
 | 
									states.Clear();
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
								Func<bool, Task> funcUpdate = async (isLiveUpdate) => {
 | 
				
			||||||
 | 
									var updateFunc = _dicExecCommandAsyncUpdate.GetOrAdd(oldinfo.entityType, t => {
 | 
				
			||||||
 | 
										var arrType = t.MakeArrayType();
 | 
				
			||||||
 | 
										var dbsetType = typeof(DbSet<>).MakeGenericType(t);
 | 
				
			||||||
 | 
										var dbsetTypeUpdate = dbsetType.GetMethod("DbContextBetchUpdateAsync", BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { arrType, typeof(bool) }, null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										var returnTarget = Expression.Label(typeof(Task<int>));
 | 
				
			||||||
 | 
										var parm1DbSet = Expression.Parameter(typeof(object));
 | 
				
			||||||
 | 
										var parm2Vals = Expression.Parameter(typeof(object[]));
 | 
				
			||||||
 | 
										var parm3IsLiveUpdate = Expression.Parameter(typeof(bool));
 | 
				
			||||||
 | 
										var var1Vals = Expression.Variable(arrType);
 | 
				
			||||||
 | 
										return Expression.Lambda<Func<object, object[], bool, Task<int>>>(Expression.Block(
 | 
				
			||||||
 | 
											new[] { var1Vals },
 | 
				
			||||||
 | 
											Expression.Assign(var1Vals, Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(arrType, parm2Vals), arrType)),
 | 
				
			||||||
 | 
											Expression.Return(returnTarget, Expression.Call(Expression.Convert(parm1DbSet, dbsetType), dbsetTypeUpdate, var1Vals, parm3IsLiveUpdate)),
 | 
				
			||||||
 | 
											Expression.Label(returnTarget, Expression.Default(typeof(Task<int>)))
 | 
				
			||||||
 | 
										), new[] { parm1DbSet, parm2Vals, parm3IsLiveUpdate }).Compile();
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									var affrows = await updateFunc(oldinfo.dbSet, states.ToArray(), isLiveUpdate);
 | 
				
			||||||
 | 
									if (affrows > 0) {
 | 
				
			||||||
 | 
										_affrows += affrows;
 | 
				
			||||||
 | 
										var islastNotUpdated = states.Count != affrows;
 | 
				
			||||||
 | 
										states.Clear();
 | 
				
			||||||
 | 
										if (islastNotUpdated) states.Add(oldinfo.state);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								while(_actions.Any() || states.Any()) {
 | 
				
			||||||
 | 
									var info = _actions.Any() ? _actions.Dequeue() : null;
 | 
				
			||||||
 | 
									if (oldinfo == null) oldinfo = info;
 | 
				
			||||||
 | 
									var isLiveUpdate = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (_actions.Any() == false && states.Any() ||
 | 
				
			||||||
 | 
										info != null && oldinfo.actionType != info.actionType ||
 | 
				
			||||||
 | 
										info != null && oldinfo.entityType != info.entityType) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										if (info != null && oldinfo.actionType == info.actionType && oldinfo.entityType == info.entityType) {
 | 
				
			||||||
 | 
											//最后一个,合起来发送
 | 
				
			||||||
 | 
											states.Add(info.state);
 | 
				
			||||||
 | 
											info = null;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										switch (oldinfo.actionType) {
 | 
				
			||||||
 | 
											case ExecCommandInfoType.Insert:
 | 
				
			||||||
 | 
												await funcInsert();
 | 
				
			||||||
 | 
												break;
 | 
				
			||||||
 | 
											case ExecCommandInfoType.Delete:
 | 
				
			||||||
 | 
												await funcDelete();
 | 
				
			||||||
 | 
												break;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										isLiveUpdate = true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (isLiveUpdate || oldinfo.actionType == ExecCommandInfoType.Update) {
 | 
				
			||||||
 | 
										if (states.Any())
 | 
				
			||||||
 | 
											await funcUpdate(isLiveUpdate);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (info != null) {
 | 
				
			||||||
 | 
										states.Add(info.state);
 | 
				
			||||||
 | 
										oldinfo = info;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,58 +1,328 @@
 | 
				
			|||||||
using System;
 | 
					using FreeSql.Internal.Model;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
using System.Collections;
 | 
					using System.Collections;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
using System.Data;
 | 
					using System.Data;
 | 
				
			||||||
using System.Data.Common;
 | 
					using System.Data.Common;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Linq.Expressions;
 | 
					using System.Linq.Expressions;
 | 
				
			||||||
using System.Text;
 | 
					using System.Text;
 | 
				
			||||||
using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FreeSql {
 | 
					namespace FreeSql {
 | 
				
			||||||
	public abstract class DbSet<TEntity> where TEntity : class {
 | 
						public abstract partial class DbSet<TEntity> where TEntity : class {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		protected DbContext _ctx;
 | 
							protected DbContext _ctx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public ISelect<TEntity> Select => _ctx._fsql.Select<TEntity>().WithTransaction(_ctx.GetOrBeginTransaction(false));
 | 
							protected ISelect<TEntity> OrmSelect(object dywhere) => _ctx._fsql.Select<TEntity>(dywhere).WithTransaction(_ctx.GetOrBeginTransaction(false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public IInsert<TEntity> Insert(TEntity source) => _ctx._fsql.Insert<TEntity>(source).WithTransaction(_ctx.GetOrBeginTransaction());
 | 
							protected IInsert<TEntity> OrmInsert() => _ctx._fsql.Insert<TEntity>().WithTransaction(_ctx.GetOrBeginTransaction());
 | 
				
			||||||
		public IInsert<TEntity> Insert(TEntity[] source) => _ctx._fsql.Insert<TEntity>(source).WithTransaction(_ctx.GetOrBeginTransaction());
 | 
							protected IInsert<TEntity> OrmInsert(TEntity source) => _ctx._fsql.Insert<TEntity>(source).WithTransaction(_ctx.GetOrBeginTransaction());
 | 
				
			||||||
		public IInsert<TEntity> Insert(IEnumerable<TEntity> source) => _ctx._fsql.Insert<TEntity>(source).WithTransaction(_ctx.GetOrBeginTransaction());
 | 
							protected IInsert<TEntity> OrmInsert(TEntity[] source) => _ctx._fsql.Insert<TEntity>(source).WithTransaction(_ctx.GetOrBeginTransaction());
 | 
				
			||||||
 | 
							protected IInsert<TEntity> OrmInsert(IEnumerable<TEntity> source) => _ctx._fsql.Insert<TEntity>(source).WithTransaction(_ctx.GetOrBeginTransaction());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public IUpdate<TEntity> Update => _ctx._fsql.Update<TEntity>().WithTransaction(_ctx.GetOrBeginTransaction());
 | 
							protected IUpdate<TEntity> OrmUpdate(object dywhere) => _ctx._fsql.Update<TEntity>(dywhere).WithTransaction(_ctx.GetOrBeginTransaction());
 | 
				
			||||||
		public IDelete<TEntity> Delete => _ctx._fsql.Delete<TEntity>().WithTransaction(_ctx.GetOrBeginTransaction());
 | 
							protected IDelete<TEntity> OrmDelete(object dywhere) => _ctx._fsql.Delete<TEntity>(dywhere).WithTransaction(_ctx.GetOrBeginTransaction());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//protected Dictionary<string, TEntity> _vals = new Dictionary<string, TEntity>();
 | 
							public ISelect<TEntity> Select => this.OrmSelect(null);
 | 
				
			||||||
		//protected tableinfo
 | 
							public ISelect<TEntity> Where(Expression<Func<TEntity, bool>> exp) => this.OrmSelect(null).Where(exp);
 | 
				
			||||||
 | 
							public ISelect<TEntity> WhereIf(bool condition, Expression<Func<TEntity, bool>> exp) => this.OrmSelect(null).WhereIf(condition, exp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//public void Add(TEntity source) {
 | 
							protected Dictionary<string, TEntity> _vals = new Dictionary<string, TEntity>();
 | 
				
			||||||
 | 
							TableInfo _tablePriv;
 | 
				
			||||||
 | 
							protected TableInfo _table => _tablePriv ?? (_tablePriv = _ctx._orm.CodeFirst.GetTableByEntity(_entityType));
 | 
				
			||||||
 | 
							protected Type _entityType = typeof(TEntity);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//}
 | 
							static ConcurrentDictionary<Type, Func<TEntity, string>> _dicGetEntityKeyString = new ConcurrentDictionary<Type, Func<TEntity, string>>();
 | 
				
			||||||
		//public void AddRange(TEntity[] source) {
 | 
							static MethodInfo MethodStringBuilderAppend = typeof(StringBuilder).GetMethod("Append", new Type[] { typeof(object) });
 | 
				
			||||||
 | 
							static MethodInfo MethodStringBuilderToString = typeof(StringBuilder).GetMethod("ToString", new Type[0]);
 | 
				
			||||||
 | 
							static PropertyInfo MethodStringBuilderLength = typeof(StringBuilder).GetProperty("Length");
 | 
				
			||||||
 | 
							static MethodInfo MethodStringConcat = typeof(string).GetMethod("Concat", new Type[]{ typeof(object) });
 | 
				
			||||||
 | 
							string GetEntityKeyString(TEntity item) {
 | 
				
			||||||
 | 
								var func = _dicGetEntityKeyString.GetOrAdd(_entityType, t => {
 | 
				
			||||||
 | 
									var pks = _table.Primarys;
 | 
				
			||||||
 | 
									var returnTarget = Expression.Label(typeof(string));
 | 
				
			||||||
 | 
									var parm1 = Expression.Parameter(_entityType);
 | 
				
			||||||
 | 
									var var1Sb = Expression.Variable(typeof(StringBuilder));
 | 
				
			||||||
 | 
									var var3IsNull = Expression.Variable(typeof(bool));
 | 
				
			||||||
 | 
									var exps = new List<Expression>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//}
 | 
									exps.AddRange(new Expression[] {
 | 
				
			||||||
		//public void AddRange(IEnumerable<TEntity> source) {
 | 
										Expression.Assign(var1Sb, Expression.New(typeof(StringBuilder))),
 | 
				
			||||||
 | 
										Expression.Assign(var3IsNull, Expression.Constant(false))
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									for (var a = 0; a < pks.Length; a++) {
 | 
				
			||||||
 | 
										exps.Add(
 | 
				
			||||||
 | 
											Expression.IfThen(
 | 
				
			||||||
 | 
												Expression.Equal(var3IsNull, Expression.Constant(false)),
 | 
				
			||||||
 | 
												Expression.IfThenElse(
 | 
				
			||||||
 | 
													Expression.Equal(Expression.MakeMemberAccess(parm1, _table.Properties[pks[a].CsName]), Expression.Default(pks[a].CsType)),
 | 
				
			||||||
 | 
													Expression.Assign(var3IsNull, Expression.Constant(true)),
 | 
				
			||||||
 | 
													Expression.Block(
 | 
				
			||||||
 | 
														new Expression[]{
 | 
				
			||||||
 | 
															a > 0 ? Expression.Call(var1Sb, MethodStringBuilderAppend, Expression.Constant("*|_,,_|*" )) : null,
 | 
				
			||||||
 | 
															Expression.Call(var1Sb, MethodStringBuilderAppend,
 | 
				
			||||||
 | 
																Expression.Convert(Expression.MakeMemberAccess(parm1, _table.Properties[pks[a].CsName]), typeof(object))
 | 
				
			||||||
 | 
															)
 | 
				
			||||||
 | 
														}.Where(c => c != null).ToArray()
 | 
				
			||||||
 | 
													)
 | 
				
			||||||
 | 
												)
 | 
				
			||||||
 | 
											)
 | 
				
			||||||
 | 
										);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									exps.Add(
 | 
				
			||||||
 | 
										Expression.IfThen(
 | 
				
			||||||
 | 
											Expression.Equal(var3IsNull, Expression.Constant(false)),
 | 
				
			||||||
 | 
											Expression.Return(returnTarget, Expression.Call(var1Sb, MethodStringBuilderToString))
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									exps.Add(Expression.Label(returnTarget, Expression.Default(typeof(string))));
 | 
				
			||||||
 | 
									return Expression.Lambda<Func<TEntity, string>>(Expression.Block(new[] { var1Sb, var3IsNull }, exps), new[] { parm1 }).Compile();
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								return func(item);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//}
 | 
							static ConcurrentDictionary<Type, Action<TEntity, TEntity>> _dicCopyNewValueToEntity = new ConcurrentDictionary<Type, Action<TEntity, TEntity>>();
 | 
				
			||||||
		//public void Update(TEntity source) {
 | 
							void CopyNewValueToEntity(TEntity old, TEntity newvalue) {
 | 
				
			||||||
 | 
								var func = _dicCopyNewValueToEntity.GetOrAdd(_entityType, t => {
 | 
				
			||||||
 | 
									var parm1 = Expression.Parameter(_entityType);
 | 
				
			||||||
 | 
									var parm2 = Expression.Parameter(_entityType);
 | 
				
			||||||
 | 
									var exps = new List<Expression>();
 | 
				
			||||||
 | 
									foreach (var prop in _table.Properties.Values) {
 | 
				
			||||||
 | 
										if (_table.ColumnsByCs.ContainsKey(prop.Name)) {
 | 
				
			||||||
 | 
											exps.Add(
 | 
				
			||||||
 | 
												Expression.Assign(
 | 
				
			||||||
 | 
													Expression.MakeMemberAccess(parm1, prop),
 | 
				
			||||||
 | 
													Expression.MakeMemberAccess(parm2, prop)
 | 
				
			||||||
 | 
												)
 | 
				
			||||||
 | 
											);
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											exps.Add(
 | 
				
			||||||
 | 
												Expression.Assign(
 | 
				
			||||||
 | 
													Expression.MakeMemberAccess(parm1, prop),
 | 
				
			||||||
 | 
													Expression.Default(prop.PropertyType)
 | 
				
			||||||
 | 
												)
 | 
				
			||||||
 | 
											);
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return Expression.Lambda<Action<TEntity, TEntity>>(Expression.Block(exps), new[] { parm1, parm2 }).Compile();
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								func(old, newvalue);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//}
 | 
							static ConcurrentDictionary<Type, Action<TEntity, long>> _dicSetEntityIdentityValue = new ConcurrentDictionary<Type, Action<TEntity, long>>();
 | 
				
			||||||
		//public void UpdateRange(TEntity[] source) {
 | 
							void SetEntityIdentityValue(TEntity old, long idtval) {
 | 
				
			||||||
 | 
								var func = _dicSetEntityIdentityValue.GetOrAdd(_entityType, t => {
 | 
				
			||||||
 | 
									var parm1 = Expression.Parameter(_entityType);
 | 
				
			||||||
 | 
									var parm2 = Expression.Parameter(typeof(long));
 | 
				
			||||||
 | 
									var exps = new List<Expression>();
 | 
				
			||||||
 | 
									exps.Add(
 | 
				
			||||||
 | 
										Expression.Assign(
 | 
				
			||||||
 | 
											Expression.MakeMemberAccess(parm1, _table.Properties[_table.Primarys[0].CsName]),
 | 
				
			||||||
 | 
											Expression.Convert(FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(_table.Primarys[0].CsType, Expression.Convert(parm2, typeof(object))), _table.Primarys[0].CsType)
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									);
 | 
				
			||||||
 | 
									return Expression.Lambda<Action<TEntity, long>>(Expression.Block(exps), new[] { parm1, parm2 }).Compile();
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								func(old, idtval);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//}
 | 
							public void Add(TEntity source) {
 | 
				
			||||||
		//public void UpdateRange(IEnumerable<TEntity> source) {
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								var key = GetEntityKeyString(source);
 | 
				
			||||||
 | 
								TEntity newval = null;
 | 
				
			||||||
 | 
								if (string.IsNullOrEmpty(key)) {
 | 
				
			||||||
 | 
									var ids = _table.Primarys.Where(a => a.Attribute.IsIdentity).ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//}
 | 
									switch(_ctx._orm.Ado.DataType) {
 | 
				
			||||||
		//public void Remove(TEntity source) {
 | 
										case DataType.SqlServer:
 | 
				
			||||||
 | 
										case DataType.PostgreSQL:
 | 
				
			||||||
 | 
											if (ids.Length == 1 && _table.Primarys.Length == 1) {
 | 
				
			||||||
 | 
												_ctx.ExecCommand();
 | 
				
			||||||
 | 
												var idtval = this.OrmInsert(source).ExecuteIdentity();
 | 
				
			||||||
 | 
												_ctx._affrows++;
 | 
				
			||||||
 | 
												SetEntityIdentityValue(source, idtval);
 | 
				
			||||||
 | 
											} else {
 | 
				
			||||||
 | 
												_ctx.ExecCommand();
 | 
				
			||||||
 | 
												newval = this.OrmInsert(source).ExecuteInserted().First();
 | 
				
			||||||
 | 
												_ctx._affrows++;
 | 
				
			||||||
 | 
												CopyNewValueToEntity(source, newval);
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
										case DataType.MySql:
 | 
				
			||||||
 | 
										case DataType.Oracle:
 | 
				
			||||||
 | 
										case DataType.Sqlite:
 | 
				
			||||||
 | 
											if (ids.Length == 1 && _table.Primarys.Length == 1) {
 | 
				
			||||||
 | 
												_ctx.ExecCommand();
 | 
				
			||||||
 | 
												var idtval = this.OrmInsert(source).ExecuteIdentity();
 | 
				
			||||||
 | 
												_ctx._affrows++;
 | 
				
			||||||
 | 
												SetEntityIdentityValue(source, idtval);
 | 
				
			||||||
 | 
											} else {
 | 
				
			||||||
 | 
												throw new Exception("DbSet.Add 失败,由于实体没有主键值,或者没有配置自增,或者自增列数不为1。");
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//}
 | 
									key = GetEntityKeyString(source);
 | 
				
			||||||
		//public void RemoveRange(TEntity[] source) {
 | 
								} else {
 | 
				
			||||||
 | 
									if (_vals.ContainsKey(key))
 | 
				
			||||||
 | 
										throw new Exception("DbSet.Add 失败,实体数据已存在,请勿重复添加。");
 | 
				
			||||||
 | 
									_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, _entityType, this, source);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (newval == null) {
 | 
				
			||||||
 | 
									newval = Activator.CreateInstance<TEntity>();
 | 
				
			||||||
 | 
									CopyNewValueToEntity(newval, source);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								_vals.Add(key, newval);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							public void AddRange(TEntity[] source) {
 | 
				
			||||||
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								for (var a = 0; a < source.Length; a++)
 | 
				
			||||||
 | 
									Add(source[a]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							public void AddRange(IEnumerable<TEntity> source) {
 | 
				
			||||||
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								foreach(var item in source)
 | 
				
			||||||
 | 
									Add(item);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//}
 | 
							static ConcurrentDictionary<Type, Func<TEntity, TEntity, string>> _dicCompareUpdateIngoreColumns = new ConcurrentDictionary<Type, Func<TEntity, TEntity, string>>();
 | 
				
			||||||
		//public void RemoveRange(IEnumerable<TEntity> source) {
 | 
							string CompareUpdateIngoreColumns(TEntity up, TEntity oldval) {
 | 
				
			||||||
 | 
								var func = _dicCompareUpdateIngoreColumns.GetOrAdd(_entityType, t => {
 | 
				
			||||||
 | 
									var returnTarget = Expression.Label(typeof(string));
 | 
				
			||||||
 | 
									var parm1 = Expression.Parameter(_entityType);
 | 
				
			||||||
 | 
									var parm2 = Expression.Parameter(_entityType);
 | 
				
			||||||
 | 
									var var1Sb = Expression.Variable(typeof(StringBuilder));
 | 
				
			||||||
 | 
									var exps = new List<Expression>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//}
 | 
									exps.AddRange(new Expression[] {
 | 
				
			||||||
 | 
										Expression.Assign(var1Sb, Expression.New(typeof(StringBuilder)))
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
									var a = 0;
 | 
				
			||||||
 | 
									foreach (var prop in _table.Properties.Values) {
 | 
				
			||||||
 | 
										if (_table.ColumnsByCs.TryGetValue(prop.Name, out var trycol) == false) continue;
 | 
				
			||||||
 | 
										exps.Add(
 | 
				
			||||||
 | 
											Expression.IfThen(
 | 
				
			||||||
 | 
												Expression.Equal(
 | 
				
			||||||
 | 
													Expression.MakeMemberAccess(parm1, prop),
 | 
				
			||||||
 | 
													Expression.MakeMemberAccess(parm2, prop)
 | 
				
			||||||
 | 
												),
 | 
				
			||||||
 | 
												Expression.Block(
 | 
				
			||||||
 | 
													new Expression[]{
 | 
				
			||||||
 | 
														a > 0 ? Expression.Call(var1Sb, MethodStringBuilderAppend, Expression.Constant(", " )) : null,
 | 
				
			||||||
 | 
														Expression.Call(var1Sb, MethodStringBuilderAppend, Expression.Constant(trycol.Attribute.Name))
 | 
				
			||||||
 | 
													}.Where(c => c != null).ToArray()
 | 
				
			||||||
 | 
												)
 | 
				
			||||||
 | 
											)
 | 
				
			||||||
 | 
										);
 | 
				
			||||||
 | 
										a++;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									exps.Add(Expression.Return(returnTarget, Expression.Call(var1Sb, MethodStringBuilderToString)));
 | 
				
			||||||
 | 
									exps.Add(Expression.Label(returnTarget, Expression.Default(typeof(string))));
 | 
				
			||||||
 | 
									return Expression.Lambda<Func<TEntity, TEntity, string>>(Expression.Block(new[] { var1Sb }, exps), new[] { parm1, parm2 }).Compile();
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
								return func(up, oldval);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							int DbContextBetchUpdate(TEntity[] 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var lstkey1 = GetEntityKeyString(uplst1);
 | 
				
			||||||
 | 
								if (_vals.TryGetValue(lstkey1, out var lstval1) == false) throw new Exception("DbSet.Update 失败,实体应该先查询再修改。");
 | 
				
			||||||
 | 
								TEntity lstval2 = default(TEntity);
 | 
				
			||||||
 | 
								if (uplst2 != null) {
 | 
				
			||||||
 | 
									var lstkey2 = GetEntityKeyString(uplst2);
 | 
				
			||||||
 | 
									if (_vals.TryGetValue(lstkey2, out lstval2) == false) throw new Exception("DbSet.Update 失败,实体应该先查询再修改。");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var cuig1 = CompareUpdateIngoreColumns(uplst1, lstval1);
 | 
				
			||||||
 | 
								var cuig2 = uplst2 != null ? CompareUpdateIngoreColumns(uplst2, lstval2) : null;
 | 
				
			||||||
 | 
								if (uplst2 != null && string.Compare(cuig1, cuig2, true) != 0) {
 | 
				
			||||||
 | 
									//最后一个不保存
 | 
				
			||||||
 | 
									var ignores = cuig2.Split(new[] { ", " }, StringSplitOptions.None);
 | 
				
			||||||
 | 
									var source = ups.ToList();
 | 
				
			||||||
 | 
									source.RemoveAt(ups.Length - 1);
 | 
				
			||||||
 | 
									var affrows = this.OrmUpdate(null).SetSource(source).IgnoreColumns(ignores).ExecuteAffrows();
 | 
				
			||||||
 | 
									foreach(var newval in source) {
 | 
				
			||||||
 | 
										var newkey = GetEntityKeyString(newval);
 | 
				
			||||||
 | 
										if (_vals.TryGetValue(newkey, out var tryold))
 | 
				
			||||||
 | 
											CopyNewValueToEntity(tryold, newval);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return affrows;
 | 
				
			||||||
 | 
								} else if (isLiveUpdate) {
 | 
				
			||||||
 | 
									//立即保存
 | 
				
			||||||
 | 
									var ignores = cuig1.Split(new[] { ", " }, StringSplitOptions.None);
 | 
				
			||||||
 | 
									var affrows = this.OrmUpdate(null).SetSource(ups).IgnoreColumns(ignores).ExecuteAffrows();
 | 
				
			||||||
 | 
									foreach (var newval in ups) {
 | 
				
			||||||
 | 
										var newkey = GetEntityKeyString(newval);
 | 
				
			||||||
 | 
										if (_vals.TryGetValue(newkey, out var tryold))
 | 
				
			||||||
 | 
											CopyNewValueToEntity(tryold, newval);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return Math.Min(ups.Length, affrows);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								//等待下次对比再保存
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void Update(TEntity source) {
 | 
				
			||||||
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								if (_table.Primarys.Any() == false) throw new Exception("DbSet.Update 失败,实体没有主键。");
 | 
				
			||||||
 | 
								var key = GetEntityKeyString(source);
 | 
				
			||||||
 | 
								if (string.IsNullOrEmpty(key)) throw new Exception("DbSet.Update 失败,实体没有设置主键值。");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var snap = Activator.CreateInstance<TEntity>();
 | 
				
			||||||
 | 
								CopyNewValueToEntity(snap, source);
 | 
				
			||||||
 | 
								if (_vals.TryGetValue(key, out var val) == false) _vals.Add(key, snap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Update, _entityType, this, snap);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							public void UpdateRange(TEntity[] source) {
 | 
				
			||||||
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								for (var a = 0; a < source.Length; a++)
 | 
				
			||||||
 | 
									Update(source[a]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							public void UpdateRange(IEnumerable<TEntity> source) {
 | 
				
			||||||
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								foreach (var item in source)
 | 
				
			||||||
 | 
									Update(item);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							int DbContextBetchRemove(TEntity[] dels) {
 | 
				
			||||||
 | 
								if (dels.Any() == false) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var affrows = this.OrmDelete(dels).ExecuteAffrows();
 | 
				
			||||||
 | 
								foreach(var del in dels) {
 | 
				
			||||||
 | 
									var key = GetEntityKeyString(del);
 | 
				
			||||||
 | 
									_vals.Remove(key);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return affrows;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							public void Remove(TEntity source) {
 | 
				
			||||||
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								if (_table.Primarys.Any() == false) throw new Exception("DbSet.Remove 失败,实体没有主键。");
 | 
				
			||||||
 | 
								var key = GetEntityKeyString(source);
 | 
				
			||||||
 | 
								if (string.IsNullOrEmpty(key)) throw new Exception("DbSet.Remove 失败,实体没有设置主键值。");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var snap = Activator.CreateInstance<TEntity>();
 | 
				
			||||||
 | 
								CopyNewValueToEntity(snap, source);
 | 
				
			||||||
 | 
								if (_vals.TryGetValue(key, out var val) == false) _vals.Add(key, snap);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Delete, _entityType, this, snap);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							public void RemoveRange(TEntity[] source) {
 | 
				
			||||||
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								for (var a = 0; a < source.Length; a++)
 | 
				
			||||||
 | 
									Remove(source[a]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							public void RemoveRange(IEnumerable<TEntity> source) {
 | 
				
			||||||
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								foreach (var item in source)
 | 
				
			||||||
 | 
									Remove(item);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	internal class BaseDbSet<TEntity> : DbSet<TEntity> where TEntity : class {
 | 
						internal class BaseDbSet<TEntity> : DbSet<TEntity> where TEntity : class {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										129
									
								
								FreeSql.DbContext/DbSetAsync.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								FreeSql.DbContext/DbSetAsync.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					using FreeSql.Internal.Model;
 | 
				
			||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
 | 
					using System.Data;
 | 
				
			||||||
 | 
					using System.Data.Common;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Linq.Expressions;
 | 
				
			||||||
 | 
					using System.Text;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace FreeSql {
 | 
				
			||||||
 | 
						partial class DbSet<TEntity> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							async public Task AddAsync(TEntity source) {
 | 
				
			||||||
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								var key = GetEntityKeyString(source);
 | 
				
			||||||
 | 
								TEntity newval = null;
 | 
				
			||||||
 | 
								if (string.IsNullOrEmpty(key)) {
 | 
				
			||||||
 | 
									var ids = _table.Primarys.Where(a => a.Attribute.IsIdentity).ToArray();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									switch (_ctx._orm.Ado.DataType) {
 | 
				
			||||||
 | 
										case DataType.SqlServer:
 | 
				
			||||||
 | 
										case DataType.PostgreSQL:
 | 
				
			||||||
 | 
											if (ids.Length == 1 && _table.Primarys.Length == 1) {
 | 
				
			||||||
 | 
												await _ctx.ExecCommandAsync();
 | 
				
			||||||
 | 
												var idtval = await this.OrmInsert(source).ExecuteIdentityAsync();
 | 
				
			||||||
 | 
												_ctx._affrows++;
 | 
				
			||||||
 | 
												SetEntityIdentityValue(source, idtval);
 | 
				
			||||||
 | 
											} else {
 | 
				
			||||||
 | 
												await _ctx.ExecCommandAsync();
 | 
				
			||||||
 | 
												newval = (await this.OrmInsert(source).ExecuteInsertedAsync()).First();
 | 
				
			||||||
 | 
												_ctx._affrows++;
 | 
				
			||||||
 | 
												CopyNewValueToEntity(source, newval);
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
										case DataType.MySql:
 | 
				
			||||||
 | 
										case DataType.Oracle:
 | 
				
			||||||
 | 
										case DataType.Sqlite:
 | 
				
			||||||
 | 
											if (ids.Length == 1 && _table.Primarys.Length == 1) {
 | 
				
			||||||
 | 
												await _ctx.ExecCommandAsync();
 | 
				
			||||||
 | 
												var idtval = await this.OrmInsert(source).ExecuteIdentityAsync();
 | 
				
			||||||
 | 
												_ctx._affrows++;
 | 
				
			||||||
 | 
												SetEntityIdentityValue(source, idtval);
 | 
				
			||||||
 | 
											} else {
 | 
				
			||||||
 | 
												throw new Exception("DbSet.Add 失败,由于实体没有主键值,或者没有配置自增,或者自增列数不为1。");
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											break;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									key = GetEntityKeyString(source);
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if (_vals.ContainsKey(key))
 | 
				
			||||||
 | 
										throw new Exception("DbSet.Add 失败,实体数据已存在,请勿重复添加。");
 | 
				
			||||||
 | 
									_ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, _entityType, this, source);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (newval == null) {
 | 
				
			||||||
 | 
									newval = Activator.CreateInstance<TEntity>();
 | 
				
			||||||
 | 
									CopyNewValueToEntity(newval, source);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								_vals.Add(key, newval);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							async public Task AddRangeAsync(TEntity[] source) {
 | 
				
			||||||
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								for (var a = 0; a < source.Length; a++)
 | 
				
			||||||
 | 
									await AddAsync(source[a]);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							async public Task AddRangeAsync(IEnumerable<TEntity> source) {
 | 
				
			||||||
 | 
								if (source == null) throw new ArgumentNullException(nameof(source));
 | 
				
			||||||
 | 
								foreach (var item in source)
 | 
				
			||||||
 | 
									await AddAsync(item);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							async Task<int> DbContextBetchUpdateAsync(TEntity[] 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var lstkey1 = GetEntityKeyString(uplst1);
 | 
				
			||||||
 | 
								if (_vals.TryGetValue(lstkey1, out var lstval1) == false) throw new Exception("DbSet.Update 失败,实体应该先查询再修改。");
 | 
				
			||||||
 | 
								TEntity lstval2 = default(TEntity);
 | 
				
			||||||
 | 
								if (uplst2 != null) {
 | 
				
			||||||
 | 
									var lstkey2 = GetEntityKeyString(uplst2);
 | 
				
			||||||
 | 
									if (_vals.TryGetValue(lstkey2, out lstval2) == false) throw new Exception("DbSet.Update 失败,实体应该先查询再修改。");
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var cuig1 = CompareUpdateIngoreColumns(uplst1, lstval1);
 | 
				
			||||||
 | 
								var cuig2 = uplst2 != null ? CompareUpdateIngoreColumns(uplst2, lstval2) : null;
 | 
				
			||||||
 | 
								if (uplst2 != null && string.Compare(cuig1, cuig2, true) != 0) {
 | 
				
			||||||
 | 
									//最后一个不保存
 | 
				
			||||||
 | 
									var ignores = cuig2.Split(new[] { ", " }, StringSplitOptions.None);
 | 
				
			||||||
 | 
									var source = ups.ToList();
 | 
				
			||||||
 | 
									source.RemoveAt(ups.Length - 1);
 | 
				
			||||||
 | 
									var affrows = await this.OrmUpdate(null).SetSource(source).IgnoreColumns(ignores).ExecuteAffrowsAsync();
 | 
				
			||||||
 | 
									foreach (var newval in source) {
 | 
				
			||||||
 | 
										var newkey = GetEntityKeyString(newval);
 | 
				
			||||||
 | 
										if (_vals.TryGetValue(newkey, out var tryold))
 | 
				
			||||||
 | 
											CopyNewValueToEntity(tryold, newval);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return affrows;
 | 
				
			||||||
 | 
								} else if (isLiveUpdate) {
 | 
				
			||||||
 | 
									//立即保存
 | 
				
			||||||
 | 
									var ignores = cuig1.Split(new[] { ", " }, StringSplitOptions.None);
 | 
				
			||||||
 | 
									var affrows = await this.OrmUpdate(null).SetSource(ups).IgnoreColumns(ignores).ExecuteAffrowsAsync();
 | 
				
			||||||
 | 
									foreach (var newval in ups) {
 | 
				
			||||||
 | 
										var newkey = GetEntityKeyString(newval);
 | 
				
			||||||
 | 
										if (_vals.TryGetValue(newkey, out var tryold))
 | 
				
			||||||
 | 
											CopyNewValueToEntity(tryold, newval);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return Math.Min(ups.Length, affrows);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								//等待下次对比再保存
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							async Task<int> DbContextBetchRemoveAsync(TEntity[] dels) {
 | 
				
			||||||
 | 
								if (dels.Any() == false) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								var affrows = await this.OrmDelete(dels).ExecuteAffrowsAsync();
 | 
				
			||||||
 | 
								foreach (var del in dels) {
 | 
				
			||||||
 | 
									var key = GetEntityKeyString(del);
 | 
				
			||||||
 | 
									_vals.Remove(key);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return affrows;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	<PropertyGroup>
 | 
						<PropertyGroup>
 | 
				
			||||||
		<TargetFramework>netstandard2.0</TargetFramework>
 | 
							<TargetFramework>netstandard2.0</TargetFramework>
 | 
				
			||||||
		<Version>0.3.20</Version>
 | 
							<Version>0.3.21</Version>
 | 
				
			||||||
		<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
 | 
							<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
 | 
				
			||||||
		<Authors>YeXiangQin</Authors>
 | 
							<Authors>YeXiangQin</Authors>
 | 
				
			||||||
		<Description>FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite.</Description>
 | 
							<Description>FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite.</Description>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	<PropertyGroup>
 | 
						<PropertyGroup>
 | 
				
			||||||
		<TargetFramework>netstandard2.0</TargetFramework>
 | 
							<TargetFramework>netstandard2.0</TargetFramework>
 | 
				
			||||||
		<Version>0.3.20</Version>
 | 
							<Version>0.3.21</Version>
 | 
				
			||||||
		<Authors>YeXiangQin</Authors>
 | 
							<Authors>YeXiangQin</Authors>
 | 
				
			||||||
		<Description>FreeSql Implementation of General Repository, Support MySql/SqlServer/PostgreSQL/Oracle/Sqlite, and read/write separation、and split table.</Description>
 | 
							<Description>FreeSql Implementation of General Repository, Support MySql/SqlServer/PostgreSQL/Oracle/Sqlite, and read/write separation、and split table.</Description>
 | 
				
			||||||
		<PackageProjectUrl>https://github.com/2881099/FreeSql/wiki/Repository</PackageProjectUrl>
 | 
							<PackageProjectUrl>https://github.com/2881099/FreeSql/wiki/Repository</PackageProjectUrl>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,10 +44,10 @@ namespace FreeSql.Tests {
 | 
				
			|||||||
		public void Test1() {
 | 
							public void Test1() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			using (var ctx = new OrderContext()) {
 | 
								using (var ctx = new OrderContext()) {
 | 
				
			||||||
				ctx.Orders.Insert(new Order { }).ExecuteAffrows();
 | 
									//ctx.Orders.OrmInsert(new Order { }).ExecuteAffrows();
 | 
				
			||||||
				ctx.Orders.Delete.Where(a => a.Id > 0).ExecuteAffrows();
 | 
									//ctx.Orders.OrmDelete.Where(a => a.Id > 0).ExecuteAffrows();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				ctx.OrderDetails.Select.Where(dt => dt.Order.Id == 10).ToList();
 | 
									//ctx.OrderDetails.OrmSelect.Where(dt => dt.Order.Id == 10).ToList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				ctx.SaveChanges();
 | 
									ctx.SaveChanges();
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,6 +17,12 @@ namespace FreeSql.DataAnnotations {
 | 
				
			|||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		public string SelectFilter { get; set; }
 | 
							public string SelectFilter { get; set; }
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
 | 
							internal bool? _RowVersion;
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// 修改/删除时,启用行版本检查
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public bool RowVersion { get => _RowVersion ?? false; set => _RowVersion = value; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		internal ConcurrentDictionary<string, ColumnAttribute> _columns { get; } = new ConcurrentDictionary<string, ColumnAttribute>(StringComparer.CurrentCultureIgnoreCase);
 | 
							internal ConcurrentDictionary<string, ColumnAttribute> _columns { get; } = new ConcurrentDictionary<string, ColumnAttribute>(StringComparer.CurrentCultureIgnoreCase);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	<PropertyGroup>
 | 
						<PropertyGroup>
 | 
				
			||||||
		<TargetFramework>netstandard2.0</TargetFramework>
 | 
							<TargetFramework>netstandard2.0</TargetFramework>
 | 
				
			||||||
		<Version>0.3.20</Version>
 | 
							<Version>0.3.21</Version>
 | 
				
			||||||
		<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
 | 
							<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
 | 
				
			||||||
		<Authors>YeXiangQin</Authors>
 | 
							<Authors>YeXiangQin</Authors>
 | 
				
			||||||
		<Description>FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite.</Description>
 | 
							<Description>FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite.</Description>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -38,6 +38,12 @@ namespace FreeSql {
 | 
				
			|||||||
		/// <param name="columns">lambda选择列</param>
 | 
							/// <param name="columns">lambda选择列</param>
 | 
				
			||||||
		/// <returns></returns>
 | 
							/// <returns></returns>
 | 
				
			||||||
		IUpdate<T1> IgnoreColumns(Expression<Func<T1, object>> columns);
 | 
							IUpdate<T1> IgnoreColumns(Expression<Func<T1, object>> columns);
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// 忽略的列
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="columns"></param>
 | 
				
			||||||
 | 
							/// <returns></returns>
 | 
				
			||||||
 | 
							IUpdate<T1> IgnoreColumns(string[] columns);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// 设置列的新值,Set(a => a.Name, "newvalue")
 | 
							/// 设置列的新值,Set(a => a.Name, "newvalue")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
using FreeSql.DataAnnotations;
 | 
					using FreeSql.DataAnnotations;
 | 
				
			||||||
 | 
					using FreeSql.Internal.Model;
 | 
				
			||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FreeSql {
 | 
					namespace FreeSql {
 | 
				
			||||||
@@ -81,5 +82,11 @@ namespace FreeSql {
 | 
				
			|||||||
		/// <param name="type"></param>
 | 
							/// <param name="type"></param>
 | 
				
			||||||
		/// <returns>未使用ConfigEntity配置时,返回null</returns>
 | 
							/// <returns>未使用ConfigEntity配置时,返回null</returns>
 | 
				
			||||||
		TableAttribute GetConfigEntity(Type type);
 | 
							TableAttribute GetConfigEntity(Type type);
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// 获取实体类核心配置
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="type"></param>
 | 
				
			||||||
 | 
							/// <returns></returns>
 | 
				
			||||||
 | 
							TableInfo GetTableByEntity(Type type);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -395,7 +395,7 @@ namespace FreeSql.Internal.CommonProvider {
 | 
				
			|||||||
						Expression.Assign(readExp, readExpAssign),
 | 
											Expression.Assign(readExp, readExpAssign),
 | 
				
			||||||
						Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp),
 | 
											Expression.IfThen(Expression.GreaterThan(readExpDataIndex, dataIndexExp),
 | 
				
			||||||
							Expression.Assign(dataIndexExp, readExpDataIndex)),
 | 
												Expression.Assign(dataIndexExp, readExpDataIndex)),
 | 
				
			||||||
						Expression.Call(typeof(Trace).GetMethod("WriteLine", new Type[]{typeof(string)}), Expression.Call(typeof(string).GetMethod("Concat", new Type[]{typeof(object) }), readExpValue)),
 | 
											//Expression.Call(typeof(Trace).GetMethod("WriteLine", new Type[]{typeof(string)}), Expression.Call(typeof(string).GetMethod("Concat", new Type[]{typeof(object) }), readExpValue)),
 | 
				
			||||||
						Expression.IfThen(Expression.NotEqual(readExpValue, Expression.Constant(null)),
 | 
											Expression.IfThen(Expression.NotEqual(readExpValue, Expression.Constant(null)),
 | 
				
			||||||
							//Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)),
 | 
												//Expression.Call(retExp, propGetSetMethod, Expression.Default(prop.PropertyType)),
 | 
				
			||||||
							Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType)))
 | 
												Expression.Call(retExp, propGetSetMethod, Expression.Convert(readExpValue, prop.PropertyType)))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -63,6 +63,11 @@ namespace FreeSql.Internal.CommonProvider {
 | 
				
			|||||||
			foreach (var col in cols) _ignore.Add(col, true);
 | 
								foreach (var col in cols) _ignore.Add(col, true);
 | 
				
			||||||
			return this;
 | 
								return this;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							public IUpdate<T1> IgnoreColumns(string[] columns) {
 | 
				
			||||||
 | 
								_ignore.Clear();
 | 
				
			||||||
 | 
								foreach (var col in columns) _ignore.Add(col, true);
 | 
				
			||||||
 | 
								return this;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		public IUpdate<T1> SetSource(T1 source) => this.SetSource(new[] { source });
 | 
							public IUpdate<T1> SetSource(T1 source) => this.SetSource(new[] { source });
 | 
				
			||||||
		public IUpdate<T1> SetSource(IEnumerable<T1> source) {
 | 
							public IUpdate<T1> SetSource(IEnumerable<T1> source) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
using System;
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FreeSql.Internal.Model {
 | 
					namespace FreeSql.Internal.Model {
 | 
				
			||||||
	class ColumnInfo {
 | 
						public class ColumnInfo {
 | 
				
			||||||
		public TableInfo Table { get; set; }
 | 
							public TableInfo Table { get; set; }
 | 
				
			||||||
		public string CsName { get; set; }
 | 
							public string CsName { get; set; }
 | 
				
			||||||
		public Type CsType { get; set; }
 | 
							public Type CsType { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ using System.Collections.Generic;
 | 
				
			|||||||
using System.Reflection;
 | 
					using System.Reflection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FreeSql.Internal.Model {
 | 
					namespace FreeSql.Internal.Model {
 | 
				
			||||||
	class TableInfo {
 | 
						public class TableInfo {
 | 
				
			||||||
		public Type Type { get; set; }
 | 
							public Type Type { get; set; }
 | 
				
			||||||
		public Type TypeLazy { get; set; }
 | 
							public Type TypeLazy { get; set; }
 | 
				
			||||||
		public MethodInfo TypeLazySetOrm { get; set; }
 | 
							public MethodInfo TypeLazySetOrm { get; set; }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,7 +13,7 @@ using System.Text;
 | 
				
			|||||||
using System.Text.RegularExpressions;
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FreeSql.Internal {
 | 
					namespace FreeSql.Internal {
 | 
				
			||||||
	class Utils {
 | 
						public class Utils {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		static ConcurrentDictionary<DataType, ConcurrentDictionary<Type, TableInfo>> _cacheGetTableByEntity = new ConcurrentDictionary<DataType, ConcurrentDictionary<Type, TableInfo>>();
 | 
							static ConcurrentDictionary<DataType, ConcurrentDictionary<Type, TableInfo>> _cacheGetTableByEntity = new ConcurrentDictionary<DataType, ConcurrentDictionary<Type, TableInfo>>();
 | 
				
			||||||
		internal static void RemoveTableByEntity(Type entity, CommonUtils common) {
 | 
							internal static void RemoveTableByEntity(Type entity, CommonUtils common) {
 | 
				
			||||||
@@ -997,7 +997,7 @@ namespace FreeSql.Internal {
 | 
				
			|||||||
		static MethodInfo MethodJTokenParse = typeof(JToken).GetMethod("Parse", new[] { typeof(string) });
 | 
							static MethodInfo MethodJTokenParse = typeof(JToken).GetMethod("Parse", new[] { typeof(string) });
 | 
				
			||||||
		static MethodInfo MethodJObjectParse = typeof(JObject).GetMethod("Parse", new[] { typeof(string) });
 | 
							static MethodInfo MethodJObjectParse = typeof(JObject).GetMethod("Parse", new[] { typeof(string) });
 | 
				
			||||||
		static MethodInfo MethodJArrayParse = typeof(JArray).GetMethod("Parse", new[] { typeof(string) });
 | 
							static MethodInfo MethodJArrayParse = typeof(JArray).GetMethod("Parse", new[] { typeof(string) });
 | 
				
			||||||
		internal static Expression GetDataReaderValueBlockExpression(Type type, Expression value) {
 | 
							public static Expression GetDataReaderValueBlockExpression(Type type, Expression value) {
 | 
				
			||||||
			var returnTarget = Expression.Label(typeof(object));
 | 
								var returnTarget = Expression.Label(typeof(object));
 | 
				
			||||||
			var valueExp = Expression.Variable(typeof(object), "locvalue");
 | 
								var valueExp = Expression.Variable(typeof(object), "locvalue");
 | 
				
			||||||
			Func<Expression> funcGetExpression = () => {
 | 
								Func<Expression> funcGetExpression = () => {
 | 
				
			||||||
@@ -1084,7 +1084,7 @@ namespace FreeSql.Internal {
 | 
				
			|||||||
				Expression.Label(returnTarget, Expression.Default(typeof(object)))
 | 
									Expression.Label(returnTarget, Expression.Default(typeof(object)))
 | 
				
			||||||
			);
 | 
								);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		internal static object GetDataReaderValue(Type type, object value) {
 | 
							public static object GetDataReaderValue(Type type, object value) {
 | 
				
			||||||
			if (value == null || value == DBNull.Value) return null;
 | 
								if (value == null || value == DBNull.Value) return null;
 | 
				
			||||||
			var func = _dicGetDataReaderValue.GetOrAdd(type, k1 => new ConcurrentDictionary<Type, Func<object, object>>()).GetOrAdd(value.GetType(), valueType => {
 | 
								var func = _dicGetDataReaderValue.GetOrAdd(type, k1 => new ConcurrentDictionary<Type, Func<object, object>>()).GetOrAdd(value.GetType(), valueType => {
 | 
				
			||||||
				var parmExp = Expression.Parameter(typeof(object), "value");
 | 
									var parmExp = Expression.Parameter(typeof(object), "value");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -277,5 +277,6 @@ where a.table_schema in ({0}) and a.table_name in ({1})".FormatMySql(tboldname ?
 | 
				
			|||||||
		public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
 | 
							public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
 | 
				
			||||||
		public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
 | 
							public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
 | 
				
			||||||
		public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
 | 
							public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
 | 
				
			||||||
 | 
							public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -315,5 +315,6 @@ where owner={{0}} and table_name={{1}}".FormatOracleSQL(tboldname ?? tbname);
 | 
				
			|||||||
		public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
 | 
							public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
 | 
				
			||||||
		public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
 | 
							public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
 | 
				
			||||||
		public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
 | 
							public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
 | 
				
			||||||
 | 
							public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -329,5 +329,6 @@ where pg_namespace.nspname={0} and pg_class.relname={1} and pg_constraint.contyp
 | 
				
			|||||||
		public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
 | 
							public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
 | 
				
			||||||
		public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
 | 
							public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
 | 
				
			||||||
		public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
 | 
							public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
 | 
				
			||||||
 | 
							public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -299,5 +299,6 @@ use " + database, tboldname ?? tbname);
 | 
				
			|||||||
		public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
 | 
							public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
 | 
				
			||||||
		public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
 | 
							public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
 | 
				
			||||||
		public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
 | 
							public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
 | 
				
			||||||
 | 
							public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -245,5 +245,6 @@ namespace FreeSql.Sqlite {
 | 
				
			|||||||
		public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
 | 
							public ICodeFirst ConfigEntity<T>(Action<TableFluent<T>> entity) => _commonUtils.ConfigEntity(entity);
 | 
				
			||||||
		public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
 | 
							public ICodeFirst ConfigEntity(Type type, Action<TableFluent> entity) => _commonUtils.ConfigEntity(type, entity);
 | 
				
			||||||
		public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
 | 
							public TableAttribute GetConfigEntity(Type type) => _commonUtils.GetConfigEntity(type);
 | 
				
			||||||
 | 
							public TableInfo GetTableByEntity(Type type) => _commonUtils.GetTableByEntity(type);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user