From df8845e5b1e18a05e345cb713420a843b960b3f0 Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Fri, 29 Mar 2019 21:28:43 +0800 Subject: [PATCH] ## v0.3.27 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 增加 行级锁功能,适用修改实体; - 增加 FreeSql.Repository 默认依赖注入的方式,同时保留原有 Autofac; - 优化 FreeSql.Repository Insert 逻辑,参考了 FreeSql.DbContext; - 优化 FreeSql.IUpdate 参照 IInsert 对大批量更新,拆分执行; - 修复 FreeSql.IInsert ClearData 重复利用的 bug(使用 IgnoreColumns 进行大批量插入时会发生); --- .../dbcontext_01/DbContexts/SongContext.cs | 2 +- Examples/orm_vs/Program.cs | 74 ++++- .../Properties/launchSettings.json | 27 -- Examples/repository_01/Startup.cs | 46 +-- .../DataAnnotations/VersionAttribute.cs | 8 - FreeSql.DbContext/DbContext/DbContext.cs | 10 +- FreeSql.DbContext/DbSet/DbSet.cs | 115 +++---- FreeSql.DbContext/DbSet/DbSetSync.cs | 100 ++---- .../Extenssions/DependencyInjection.cs | 2 - .../{Utils.cs => DataFilterUtil.cs} | 2 +- ...sions.cs => AutofacDependencyInjection.cs} | 2 +- .../Extenssions/DependencyInjection.cs | 39 +++ ...ons.cs => FreeSqlRepositoryExtenssions.cs} | 2 +- FreeSql.Repository/FreeSql.Repository.csproj | 2 +- .../Repository/BaseRepository.cs | 229 +++++++++++--- FreeSql/DataAnnotations/ColumnAttribute.cs | 6 +- .../Extensions/EntityUtilExtensions.cs | 46 ++- FreeSql/Interface/Curd/IUpdate.cs | 7 - .../Internal/CommonProvider/InsertProvider.cs | 94 ++++-- .../Internal/CommonProvider/UpdateProvider.cs | 290 +++++++++++++++--- FreeSql/Internal/CommonUtils.cs | 5 +- FreeSql/Internal/Model/TableInfo.cs | 1 + FreeSql/Internal/UtilsExpressionTree.cs | 5 + FreeSql/MySql/Curd/MySqlInsert.cs | 16 +- FreeSql/MySql/Curd/MySqlUpdate.cs | 14 +- FreeSql/Oracle/Curd/OracleInsert.cs | 14 +- FreeSql/Oracle/Curd/OracleUpdate.cs | 10 +- FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs | 16 +- FreeSql/PostgreSQL/Curd/PostgreSQLUpdate.cs | 14 +- FreeSql/SqlServer/Curd/SqlServerInsert.cs | 16 +- FreeSql/SqlServer/Curd/SqlServerUpdate.cs | 14 +- FreeSql/Sqlite/Curd/SqliteInsert.cs | 14 +- FreeSql/Sqlite/Curd/SqliteUpdate.cs | 10 +- 33 files changed, 831 insertions(+), 421 deletions(-) delete mode 100644 Examples/repository_01/Properties/launchSettings.json delete mode 100644 FreeSql.DbContext/DataAnnotations/VersionAttribute.cs rename FreeSql.Repository/{Utils.cs => DataFilterUtil.cs} (99%) rename FreeSql.Repository/Extenssions/{AutofacExtenssions.cs => AutofacDependencyInjection.cs} (96%) create mode 100644 FreeSql.Repository/Extenssions/DependencyInjection.cs rename FreeSql.Repository/Extenssions/{IFreeSqlExtenssions.cs => FreeSqlRepositoryExtenssions.cs} (97%) rename FreeSql.DbContext/Extenssions/EntityUtil.cs => FreeSql/Extensions/EntityUtilExtensions.cs (89%) diff --git a/Examples/dbcontext_01/DbContexts/SongContext.cs b/Examples/dbcontext_01/DbContexts/SongContext.cs index 74b4ea71..45e174d5 100644 --- a/Examples/dbcontext_01/DbContexts/SongContext.cs +++ b/Examples/dbcontext_01/DbContexts/SongContext.cs @@ -26,7 +26,7 @@ namespace dbcontext_01 { public virtual ICollection Tags { get; set; } - [Version] + [Column(IsVersion = true)] public long versionRow { get; set; } } public class Song_tag { diff --git a/Examples/orm_vs/Program.cs b/Examples/orm_vs/Program.cs index 0e394834..344074dd 100644 --- a/Examples/orm_vs/Program.cs +++ b/Examples/orm_vs/Program.cs @@ -102,6 +102,27 @@ namespace orm_vs Console.Write(sb.ToString()); sb.Clear(); + Console.WriteLine("更新:"); + Update(sb, 1000, 1); + Console.Write(sb.ToString()); + sb.Clear(); + Update(sb, 1000, 10); + Console.Write(sb.ToString()); + sb.Clear(); + + Update(sb, 1, 1000); + Console.Write(sb.ToString()); + sb.Clear(); + Update(sb, 1, 10000); + Console.Write(sb.ToString()); + sb.Clear(); + Update(sb, 1, 50000); + Console.Write(sb.ToString()); + sb.Clear(); + Update(sb, 1, 100000); + Console.Write(sb.ToString()); + sb.Clear(); + Console.WriteLine("测试结束,按任意键退出..."); Console.ReadKey(); } @@ -150,12 +171,12 @@ namespace orm_vs sw.Restart(); for (var a = 0; a < forTime; a++) { - //fsql.Insert(songs).ExecuteAffrows(); - using (var db = new FreeSongContext()) { - //db.Configuration.AutoDetectChangesEnabled = false; - db.Songs.AddRange(songs.ToArray()); - db.SaveChanges(); - } + fsql.Insert(songs).ExecuteAffrows(); + //using (var db = new FreeSongContext()) { + // //db.Configuration.AutoDetectChangesEnabled = false; + // db.Songs.AddRange(songs.ToArray()); + // db.SaveChanges(); + //} } sw.Stop(); sb.AppendLine($"FreeSql Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms"); @@ -183,7 +204,46 @@ namespace orm_vs sw.Stop(); sb.AppendLine($"EFCore Insert {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms\r\n"); } - } + + static void Update(StringBuilder sb, int forTime, int size) { + Stopwatch sw = new Stopwatch(); + + var songs = fsql.Select().Limit(size).ToList(); + sw.Restart(); + for (var a = 0; a < forTime; a++) { + fsql.Update().SetSource(songs).ExecuteAffrows(); + } + sw.Stop(); + sb.AppendLine($"FreeSql Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms"); + + songs = sugar.Queryable().Take(size).ToList(); + sw.Restart(); + Exception sugarEx = null; + try { + for (var a = 0; a < forTime; a++) + sugar.Updateable(songs).ExecuteCommand(); + } catch (Exception ex) { + sugarEx = ex; + } + sw.Stop(); + sb.AppendLine($"SqlSugar Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms" + (sugarEx != null ? $"成绩无效,错误:{sugarEx.Message}" : "")); + + using (var db = new SongContext()) { + songs = db.Songs.Take(size).AsNoTracking().ToList(); + } + sw.Restart(); + for (var a = 0; a < forTime; a++) { + + using (var db = new SongContext()) { + //db.Configuration.AutoDetectChangesEnabled = false; + db.Songs.UpdateRange(songs.ToArray()); + db.SaveChanges(); + } + } + sw.Stop(); + sb.AppendLine($"EFCore Update {size}条数据,循环{forTime}次,耗时{sw.ElapsedMilliseconds}ms\r\n"); + } + } [FreeSql.DataAnnotations.Table(Name = "freesql_song")] [SugarTable("sugar_song")] diff --git a/Examples/repository_01/Properties/launchSettings.json b/Examples/repository_01/Properties/launchSettings.json deleted file mode 100644 index ad8494da..00000000 --- a/Examples/repository_01/Properties/launchSettings.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:64150/", - "sslPort": 0 - } - }, - "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, - "repository_01": { - "commandName": "Project", - "launchBrowser": true, - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "applicationUrl": "http://localhost:64154/" - } - } -} \ No newline at end of file diff --git a/Examples/repository_01/Startup.cs b/Examples/repository_01/Startup.cs index 1442ddf9..dab5e76c 100644 --- a/Examples/repository_01/Startup.cs +++ b/Examples/repository_01/Startup.cs @@ -53,7 +53,7 @@ namespace repository_01 { public IConfiguration Configuration { get; } public static IFreeSql Fsql { get; private set; } - public IServiceProvider ConfigureServices(IServiceCollection services) { + public void ConfigureServices(IServiceCollection services) { //services.AddTransient(s => s.) @@ -67,42 +67,28 @@ namespace repository_01 { }); services.AddSingleton(Fsql); - //var baseType = typeof(IRepository); - //var freeTypes = baseType.Assembly.GetTypes().Where(t => baseType.IsAssignableFrom(t)); - //foreach (var type in freeTypes) { - // if ((type.IsInterface || type.IsAbstract) && type.IsGenericType) { - // if (type.GenericTypeArguments.Length == 1) - // services.AddScoped(type, sp => { - // return Activator.CreateInstance(typeof(GuidRepository<>).MakeGenericType(type.GenericTypeArguments[0]), sp.GetService()); - // }); - // else - // services.AddScoped(type, sp => { - // return Activator.CreateInstance(typeof(DefaultRepository<,>).MakeGenericType(type.GenericTypeArguments[0], type.GenericTypeArguments[1]), sp.GetService()); - // }); - // continue; - // } - // services.AddScoped(type); - //} - //var types = GetType().Assembly.GetTypes().Where(t => baseType.IsAssignableFrom(t) && !t.IsAbstract); - //foreach (var type in types) { - // services.AddScoped(type); - //} - - - var builder = new ContainerBuilder(); - - builder.RegisterFreeRepository(filter => filter - .Apply("test", a => a.Title == DateTime.Now.ToString() + System.Threading.Thread.CurrentThread.ManagedThreadId) + services.AddFreeRepository(filter => filter + //.Apply("test", a => a.Title == DateTime.Now.ToString() + System.Threading.Thread.CurrentThread.ManagedThreadId) .Apply("softdelete", a => a.IsDeleted == false) , this.GetType().Assembly ); - builder.Populate(services); - var container = builder.Build(); - return new AutofacServiceProvider(container); + //var builder = new ContainerBuilder(); + + //builder.RegisterFreeRepository(filter => filter + // //.Apply("test", a => a.Title == DateTime.Now.ToString() + System.Threading.Thread.CurrentThread.ManagedThreadId) + // .Apply("softdelete", a => a.IsDeleted == false) + // , + // this.GetType().Assembly + //); + + //builder.Populate(services); + //var container = builder.Build(); + + //return new AutofacServiceProvider(container); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { diff --git a/FreeSql.DbContext/DataAnnotations/VersionAttribute.cs b/FreeSql.DbContext/DataAnnotations/VersionAttribute.cs deleted file mode 100644 index b25e708f..00000000 --- a/FreeSql.DbContext/DataAnnotations/VersionAttribute.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace FreeSql { - public class VersionAttribute : Attribute { - } -} diff --git a/FreeSql.DbContext/DbContext/DbContext.cs b/FreeSql.DbContext/DbContext/DbContext.cs index fe768d1b..9bb6ddab 100644 --- a/FreeSql.DbContext/DbContext/DbContext.cs +++ b/FreeSql.DbContext/DbContext/DbContext.cs @@ -42,8 +42,15 @@ namespace FreeSql { } + + Dictionary _dicSet = new Dictionary(); public DbSet Set() where TEntity : class => this.Set(typeof(TEntity)) as DbSet; - public object Set(Type entityType) => Activator.CreateInstance(typeof(BaseDbSet<>).MakeGenericType(entityType), this); + public object Set(Type entityType) { + if (_dicSet.ContainsKey(entityType)) return _dicSet[entityType]; + var sd = Activator.CreateInstance(typeof(BaseDbSet<>).MakeGenericType(entityType), this); + _dicSet.Add(entityType, sd); + return sd; + } protected Dictionary AllSets { get; } = new Dictionary(); @@ -92,6 +99,7 @@ namespace FreeSql { } } void Rollback() { + _actions.Clear(); if (_tran != null) { try { _tran.Rollback(); diff --git a/FreeSql.DbContext/DbSet/DbSet.cs b/FreeSql.DbContext/DbSet/DbSet.cs index daf03c5a..4aee0a70 100644 --- a/FreeSql.DbContext/DbSet/DbSet.cs +++ b/FreeSql.DbContext/DbSet/DbSet.cs @@ -1,35 +1,63 @@ -using FreeSql.Internal.Model; +using FreeSql.Extensions.EntityUtil; +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; -using FreeSql.Extensions; namespace FreeSql { + + internal class BaseDbSet : DbSet where TEntity : class { + + public BaseDbSet(DbContext ctx) { + _ctx = ctx; + _fsql = ctx._fsql; + } + } + public abstract partial class DbSet where TEntity : class { internal DbContext _ctx; - IFreeSql _fsql => _ctx._fsql; + internal IFreeSql _fsql; - ISelect OrmSelect(object dywhere) { - _ctx.ExecCommand(); //查询前先提交,否则会出脏读 - return _fsql.Select(dywhere).WithTransaction(_ctx.GetOrBeginTransaction(false)).TrackToList(TrackToList); + internal ISelect OrmSelect(object dywhere) { + ExecuteCommand(); //查询前先提交,否则会出脏读 + return _fsql.Select(dywhere).WithTransaction(_ctx?.GetOrBeginTransaction(false)).TrackToList(TrackToList); } - IInsert OrmInsert() => _fsql.Insert().WithTransaction(_ctx.GetOrBeginTransaction()); - IInsert OrmInsert(TEntity data) => _fsql.Insert(data).WithTransaction(_ctx.GetOrBeginTransaction()); - IInsert OrmInsert(TEntity[] data) => _fsql.Insert(data).WithTransaction(_ctx.GetOrBeginTransaction()); - IInsert OrmInsert(IEnumerable data) => _fsql.Insert(data).WithTransaction(_ctx.GetOrBeginTransaction()); + internal virtual IInsert OrmInsert() => _fsql.Insert().WithTransaction(_ctx?.GetOrBeginTransaction()); + internal virtual IInsert OrmInsert(TEntity data) => _fsql.Insert(data).WithTransaction(_ctx?.GetOrBeginTransaction()); + internal virtual IInsert OrmInsert(TEntity[] data) => _fsql.Insert(data).WithTransaction(_ctx?.GetOrBeginTransaction()); + internal virtual IInsert OrmInsert(IEnumerable data) => _fsql.Insert(data).WithTransaction(_ctx?.GetOrBeginTransaction()); - IUpdate OrmUpdate(object dywhere) => _fsql.Update(dywhere).WithTransaction(_ctx.GetOrBeginTransaction()); - IDelete OrmDelete(object dywhere) => _fsql.Delete(dywhere).WithTransaction(_ctx.GetOrBeginTransaction()); + internal virtual IUpdate OrmUpdate(object dywhere) => _fsql.Update(dywhere).WithTransaction(_ctx?.GetOrBeginTransaction()); + internal virtual IDelete OrmDelete(object dywhere) => _fsql.Delete(dywhere).WithTransaction(_ctx?.GetOrBeginTransaction()); + + internal void EnqueueAction(DbContext.ExecCommandInfoType actionType, object dbSet, Type stateType, object state) { + _ctx?.EnqueueAction(actionType, dbSet, stateType, state); + } + internal void ExecuteCommand() { + _ctx?.ExecCommand(); + } + internal void IncrAffrows(long affrows) { + if (_ctx != null) + _ctx._affrows += affrows; + } + internal void TrackToList(object list) { + if (list == null) return; + var ls = list as IList; + if (ls == null) return; + + foreach (var item in ls) { + var key = _fsql.GetEntityKeyString(item); + if (_states.ContainsKey(key)) { + _fsql.MapEntityValue(item, _states[key].Value); + _states[key].Time = DateTime.Now; + } else { + _states.Add(key, CreateEntityState(item)); + } + } + } public ISelect Select => this.OrmSelect(null); public ISelect Where(Expression> exp) => this.OrmSelect(null).Where(exp); @@ -42,24 +70,6 @@ namespace FreeSql { ColumnInfo[] _tableIdentitys => _tableIdentitysPriv ?? (_tableIdentitysPriv = _table.Primarys.Where(a => a.Attribute.IsIdentity).ToArray()); Type _entityType = typeof(TEntity); - bool _versionColumnPrivPrivIsInit = false; - ColumnInfo _versionColumnPriv; - ColumnInfo _versionColumn { - get { - if (_versionColumnPrivPrivIsInit == false) { - var vc = _table.Properties.Where(a => _table.ColumnsByCs.ContainsKey(a.Key) && a.Value.GetCustomAttributes(typeof(VersionAttribute), false).Any()); - if (vc.Any()) { - var col = _table.ColumnsByCs[vc.Last().Key]; - if (col.CsType.IsNullableType() || col.CsType.IsNumberType() == false) - throw new Exception($"属性{col.CsName} 被标注为行级锁(Version),但其必须为数字类型,并且不可为 Nullable"); - _versionColumnPriv = col; - } - _versionColumnPrivPrivIsInit = true; - } - return _versionColumnPriv; - } - } - public class EntityState { public EntityState(TEntity value, string key) { this.Value = value; @@ -202,35 +212,12 @@ namespace FreeSql { if (isThrow) throw new Exception($"不可删除,未设置主键的值:{_fsql.GetEntityString(data)}"); return false; } - if (_states.TryGetValue(key, out var tryval) == false) { - if (isThrow) throw new Exception($"不可更新,数据未被跟踪,应该先查询:{_fsql.GetEntityString(data)}"); - return false; - } + //if (_states.TryGetValue(key, out var tryval) == false) { + // if (isThrow) throw new Exception($"不可删除,数据未被跟踪,应该先查询:{_fsql.GetEntityString(data)}"); + // return false; + //} return true; } #endregion - - void TrackToList(object list) { - if (list == null) return; - var ls = list as IList; - if (ls == null) return; - - foreach (var item in ls) { - var key = _fsql.GetEntityKeyString(item); - if (_states.ContainsKey(key)) { - _fsql.MapEntityValue(item, _states[key].Value); - _states[key].Time = DateTime.Now; - } else { - _states.Add(key, CreateEntityState(item)); - } - } - } - } - - internal class BaseDbSet : DbSet where TEntity : class { - - public BaseDbSet(DbContext ctx) { - _ctx = ctx; - } } } diff --git a/FreeSql.DbContext/DbSet/DbSetSync.cs b/FreeSql.DbContext/DbSet/DbSetSync.cs index 601949d3..3b276cfd 100644 --- a/FreeSql.DbContext/DbSet/DbSetSync.cs +++ b/FreeSql.DbContext/DbSet/DbSetSync.cs @@ -1,16 +1,7 @@ -using FreeSql.Internal.Model; +using FreeSql.Extensions.EntityUtil; 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; -using FreeSql.Extensions; namespace FreeSql { partial class DbSet { @@ -22,25 +13,25 @@ namespace FreeSql { } #region Add - void AddPriv(TEntity source, bool isCheck) { - if (isCheck && CanAdd(source, true) == false) return; + void AddPriv(TEntity data, bool isCheck) { + if (isCheck && CanAdd(data, true) == false) return; if (_tableIdentitys.Length > 0) { //有自增,马上执行 switch (_fsql.Ado.DataType) { case DataType.SqlServer: case DataType.PostgreSQL: if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) { - _ctx.ExecCommand(); - var idtval = this.OrmInsert(source).ExecuteIdentity(); - _ctx._affrows++; - _fsql.SetEntityIdentityValueWithPrimary(source, idtval); - var state = CreateEntityState(source); + ExecuteCommand(); + var idtval = this.OrmInsert(data).ExecuteIdentity(); + IncrAffrows(1); + _fsql.SetEntityIdentityValueWithPrimary(data, idtval); + var state = CreateEntityState(data); _states.Add(state.Key, state); } else { - _ctx.ExecCommand(); - var newval = this.OrmInsert(source).ExecuteInserted().First(); - _ctx._affrows++; - _fsql.MapEntityValue(newval, source); + ExecuteCommand(); + var newval = this.OrmInsert(data).ExecuteInserted().First(); + IncrAffrows(1); + _fsql.MapEntityValue(newval, data); var state = CreateEntityState(newval); _states.Add(state.Key, state); } @@ -49,21 +40,21 @@ namespace FreeSql { case DataType.Oracle: case DataType.Sqlite: if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) { - _ctx.ExecCommand(); - var idtval = this.OrmInsert(source).ExecuteIdentity(); - _ctx._affrows++; - _fsql.SetEntityIdentityValueWithPrimary(source, idtval); - var state = CreateEntityState(source); + ExecuteCommand(); + var idtval = this.OrmInsert(data).ExecuteIdentity(); + IncrAffrows(1); + _fsql.SetEntityIdentityValueWithPrimary(data, idtval); + var state = CreateEntityState(data); _states.Add(state.Key, state); } return; } } else { //进入队列,等待 SaveChanges 时执行 - _ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, this, typeof(EntityState), CreateEntityState(source)); + EnqueueAction(DbContext.ExecCommandInfoType.Insert, this, typeof(EntityState), CreateEntityState(data)); } } - public void Add(TEntity source) => AddPriv(source, true); + public void Add(TEntity data) => AddPriv(data, true); #endregion #region AddRange @@ -78,13 +69,13 @@ namespace FreeSql { switch (_fsql.Ado.DataType) { case DataType.SqlServer: case DataType.PostgreSQL: - _ctx.ExecCommand(); + ExecuteCommand(); var rets = this.OrmInsert(data).ExecuteInserted(); if (rets.Count != data.Count()) throw new Exception($"特别错误:批量添加失败,{_fsql.Ado.DataType} 的返回数据,与添加的数目不匹配"); var idx = 0; foreach (var s in data) _fsql.MapEntityValue(rets[idx++], s); - _ctx._affrows += rets.Count; + IncrAffrows(rets.Count); TrackToList(rets); return; case DataType.MySql: @@ -97,7 +88,7 @@ namespace FreeSql { } else { //进入队列,等待 SaveChanges 时执行 foreach (var s in data) - _ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, this, typeof(EntityState), CreateEntityState(s)); + EnqueueAction(DbContext.ExecCommandInfoType.Insert, this, typeof(EntityState), CreateEntityState(s)); } } public void AddRange(IEnumerable data) { @@ -111,13 +102,13 @@ namespace FreeSql { switch (_fsql.Ado.DataType) { case DataType.SqlServer: case DataType.PostgreSQL: - _ctx.ExecCommand(); + ExecuteCommand(); var rets = this.OrmInsert(data).ExecuteInserted(); if (rets.Count != data.Count()) throw new Exception($"特别错误:批量添加失败,{_fsql.Ado.DataType} 的返回数据,与添加的数目不匹配"); var idx = 0; foreach(var s in data) _fsql.MapEntityValue(rets[idx++], s); - _ctx._affrows += rets.Count; + IncrAffrows(rets.Count); TrackToList(rets); return; case DataType.MySql: @@ -130,7 +121,7 @@ namespace FreeSql { } else { //进入队列,等待 SaveChanges 时执行 foreach (var s in data) - _ctx.EnqueueAction(DbContext.ExecCommandInfoType.Insert, this, typeof(EntityState), CreateEntityState(s)); + EnqueueAction(DbContext.ExecCommandInfoType.Insert, this, typeof(EntityState), CreateEntityState(s)); } } #endregion @@ -168,42 +159,11 @@ namespace FreeSql { return data.Count; var updateSource = data.Select(a => a.Value).ToArray(); - var update = this.OrmUpdate(null).SetSource(updateSource); - - var isWhereVersion = false; - if (_versionColumn != null) { - if (cuig.Contains(_versionColumn.CsName)) { - var parm1Exp = Expression.Parameter(_entityType, "a"); - var lambdExp = Expression.Lambda( - typeof(Func<,>).MakeGenericType(_entityType, _versionColumn.CsType), - Expression.Add( - Expression.MakeMemberAccess(parm1Exp, _table.Properties[_versionColumn.CsName]), - Expression.Convert(Expression.Constant(1), _versionColumn.CsType) - ), - parm1Exp - ); - update.AppendEntityUpdateSetWithColumn(_versionColumn.CsType, lambdExp); - isWhereVersion = true; - } - } - update.IgnoreColumns(cuig); - - if (isWhereVersion) - update.WhereCaseSource(_versionColumn.CsName, sqlval => sqlval); + var update = this.OrmUpdate(null).SetSource(updateSource).IgnoreColumns(cuig); var affrows = update.ExecuteAffrows(); - if (affrows != updateSource.Length) { - if (_versionColumn != null) - throw new Exception("数据未更新,其中的记录可能不存在,或者【行级乐观锁】版本过旧"); - throw new Exception("数据未更新,其中的记录可能不存在"); - } - foreach (var newval in data) { - - if (isWhereVersion) - _fsql.SetEntityIncrByWithPropertyName(newval.Value, _versionColumn.CsName, 1); - if (_states.TryGetValue(newval.Key, out var tryold)) _fsql.MapEntityValue(newval.Value, tryold.Value); } @@ -216,7 +176,7 @@ namespace FreeSql { void UpdatePriv(TEntity data, bool isCheck) { if (isCheck && CanUpdate(data, true) == false) return; - _ctx.EnqueueAction(DbContext.ExecCommandInfoType.Update, this, typeof(EntityState), CreateEntityState(data)); + EnqueueAction(DbContext.ExecCommandInfoType.Update, this, typeof(EntityState), CreateEntityState(data)); } public void Update(TEntity data) => UpdatePriv(data, true); public void UpdateRange(TEntity[] data) { @@ -233,14 +193,14 @@ namespace FreeSql { int DbContextBetchRemove(EntityState[] dels) { if (dels.Any() == false) return 0; var affrows = this.OrmDelete(dels.Select(a => a.Value)).ExecuteAffrows(); - return affrows; + return Math.Max(dels.Length, affrows); } void RemovePriv(TEntity data, bool isCheck) { if (isCheck && CanRemove(data, true) == false) return; var state = CreateEntityState(data); - _ctx.EnqueueAction(DbContext.ExecCommandInfoType.Delete, this, typeof(EntityState), state); + EnqueueAction(DbContext.ExecCommandInfoType.Delete, this, typeof(EntityState), state); - _states.Remove(state.Key); + if (_states.ContainsKey(state.Key)) _states.Remove(state.Key); _fsql.ClearEntityPrimaryValueWithIdentityAndGuid(data); } public void Remove(TEntity data) => RemovePriv(data, true); diff --git a/FreeSql.DbContext/Extenssions/DependencyInjection.cs b/FreeSql.DbContext/Extenssions/DependencyInjection.cs index 41d49cde..75cbf032 100644 --- a/FreeSql.DbContext/Extenssions/DependencyInjection.cs +++ b/FreeSql.DbContext/Extenssions/DependencyInjection.cs @@ -1,7 +1,5 @@ using Microsoft.Extensions.DependencyInjection; using System; -using System.Collections.Generic; -using System.Text; namespace FreeSql { public static class DbContextDependencyInjection { diff --git a/FreeSql.Repository/Utils.cs b/FreeSql.Repository/DataFilterUtil.cs similarity index 99% rename from FreeSql.Repository/Utils.cs rename to FreeSql.Repository/DataFilterUtil.cs index 331a8de3..77f41a5d 100644 --- a/FreeSql.Repository/Utils.cs +++ b/FreeSql.Repository/DataFilterUtil.cs @@ -8,7 +8,7 @@ using System.Text; namespace FreeSql { - internal class Utils { + internal class DataFilterUtil { internal static Action _globalDataFilter; diff --git a/FreeSql.Repository/Extenssions/AutofacExtenssions.cs b/FreeSql.Repository/Extenssions/AutofacDependencyInjection.cs similarity index 96% rename from FreeSql.Repository/Extenssions/AutofacExtenssions.cs rename to FreeSql.Repository/Extenssions/AutofacDependencyInjection.cs index c31c62f6..038a09a8 100644 --- a/FreeSql.Repository/Extenssions/AutofacExtenssions.cs +++ b/FreeSql.Repository/Extenssions/AutofacDependencyInjection.cs @@ -20,7 +20,7 @@ public static class FreeSqlRepositoryAutofacExtenssions { static void RegisterFreeRepositoryPrivate(ContainerBuilder builder, Action globalDataFilter, params Assembly[] assemblies) { - Utils._globalDataFilter = globalDataFilter; + DataFilterUtil._globalDataFilter = globalDataFilter; builder.RegisterGeneric(typeof(GuidRepository<>)).As( typeof(GuidRepository<>), diff --git a/FreeSql.Repository/Extenssions/DependencyInjection.cs b/FreeSql.Repository/Extenssions/DependencyInjection.cs new file mode 100644 index 00000000..e258b676 --- /dev/null +++ b/FreeSql.Repository/Extenssions/DependencyInjection.cs @@ -0,0 +1,39 @@ +using FreeSql; +using System; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Linq.Expressions; +using System.Reflection; +using System.Linq; +using Microsoft.Extensions.DependencyInjection; + +namespace FreeSql { + public static class FreeSqlRepositoryDependencyInjection { + + public static IServiceCollection AddFreeRepository(this IServiceCollection services, Action globalDataFilter = null, params Assembly[] assemblies) { + + DataFilterUtil._globalDataFilter = globalDataFilter; + + services.AddScoped(typeof(IReadOnlyRepository<>), typeof(GuidRepository<>)); + services.AddScoped(typeof(IBasicRepository<>), typeof(GuidRepository<>)); + services.AddScoped(typeof(BaseRepository<>), typeof(GuidRepository<>)); + services.AddScoped(typeof(GuidRepository<>)); + + services.AddScoped(typeof(IReadOnlyRepository<,>), typeof(DefaultRepository<,>)); + services.AddScoped(typeof(IBasicRepository<,>), typeof(DefaultRepository<,>)); + services.AddScoped(typeof(BaseRepository<,>), typeof(DefaultRepository<,>)); + services.AddScoped(typeof(DefaultRepository<,>)); + + if (assemblies?.Any() == true) { + foreach(var asse in assemblies) { + foreach (var repos in asse.GetTypes().Where(a => a.IsAbstract == false && typeof(IRepository).IsAssignableFrom(a))) { + + services.AddScoped(repos); + } + } + } + + return services; + } + } +} \ No newline at end of file diff --git a/FreeSql.Repository/Extenssions/IFreeSqlExtenssions.cs b/FreeSql.Repository/Extenssions/FreeSqlRepositoryExtenssions.cs similarity index 97% rename from FreeSql.Repository/Extenssions/IFreeSqlExtenssions.cs rename to FreeSql.Repository/Extenssions/FreeSqlRepositoryExtenssions.cs index 7376e413..ab5fcccd 100644 --- a/FreeSql.Repository/Extenssions/IFreeSqlExtenssions.cs +++ b/FreeSql.Repository/Extenssions/FreeSqlRepositoryExtenssions.cs @@ -7,7 +7,7 @@ using System.Linq.Expressions; using System.Linq; using System.Data; -public static class FreeSqlRepositoryIFreeSqlExtenssions { +public static class FreeSqlRepositoryExtenssions { /// /// 返回默认仓库类 diff --git a/FreeSql.Repository/FreeSql.Repository.csproj b/FreeSql.Repository/FreeSql.Repository.csproj index 38e5b67f..3de04a42 100644 --- a/FreeSql.Repository/FreeSql.Repository.csproj +++ b/FreeSql.Repository/FreeSql.Repository.csproj @@ -15,7 +15,7 @@ - + diff --git a/FreeSql.Repository/Repository/BaseRepository.cs b/FreeSql.Repository/Repository/BaseRepository.cs index 452d6097..813dfa8a 100644 --- a/FreeSql.Repository/Repository/BaseRepository.cs +++ b/FreeSql.Repository/Repository/BaseRepository.cs @@ -1,6 +1,7 @@ -using System; +using FreeSql.Extensions.EntityUtil; +using FreeSql.Internal.Model; +using System; using System.Collections.Generic; -using System.Data.Common; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; @@ -28,7 +29,7 @@ namespace FreeSql { protected BaseRepository(IFreeSql fsql, Expression> filter, Func asTable = null) : base() { _fsql = fsql ?? throw new NullReferenceException(nameof(fsql)); - Utils.SetRepositoryDataFilter(this, null); + DataFilterUtil.SetRepositoryDataFilter(this, null); DataFilter.Apply("", filter); AsTable = asTable; } @@ -42,52 +43,20 @@ namespace FreeSql { public Task DeleteAsync(TEntity entity) => OrmDelete(entity).ExecuteAffrowsAsync(); public virtual TEntity Insert(TEntity entity) { - switch (_fsql.Ado.DataType) { - case DataType.SqlServer: - case DataType.PostgreSQL: - return OrmInsert(entity).ExecuteInserted().FirstOrDefault(); - case DataType.MySql: - case DataType.Oracle: - case DataType.Sqlite: - default: - throw new NotImplementedException($"{_fsql.Ado.DataType}不支持类似returning或output类型的特性,请参考FreeSql插入数据的方法重新实现。"); - } + Add(entity); + return entity; } public virtual List Insert(IEnumerable entitys) { - switch (_fsql.Ado.DataType) { - case DataType.SqlServer: - case DataType.PostgreSQL: - return OrmInsert(entitys).ExecuteInserted(); - case DataType.MySql: - case DataType.Oracle: - case DataType.Sqlite: - default: - throw new NotImplementedException($"{_fsql.Ado.DataType}不支持类似returning或output类型的特性,请参考FreeSql插入数据的方法重新实现。"); - } + AddRange(entitys); + return entitys.ToList(); } async public virtual Task InsertAsync(TEntity entity) { - switch (_fsql.Ado.DataType) { - case DataType.SqlServer: - case DataType.PostgreSQL: - return (await OrmInsert(entity).ExecuteInsertedAsync()).FirstOrDefault(); - case DataType.MySql: - case DataType.Oracle: - case DataType.Sqlite: - default: - throw new NotImplementedException($"{_fsql.Ado.DataType}不支持类似returning或output类型的特性,请参考FreeSql插入数据的方法重新实现。"); - } + await AddAsync(entity); + return entity; } - public virtual Task> InsertAsync(IEnumerable entitys) { - switch (_fsql.Ado.DataType) { - case DataType.SqlServer: - case DataType.PostgreSQL: - return OrmInsert(entitys).ExecuteInsertedAsync(); - case DataType.MySql: - case DataType.Oracle: - case DataType.Sqlite: - default: - throw new NotImplementedException($"{_fsql.Ado.DataType}不支持类似returning或output类型的特性,请参考FreeSql插入数据的方法重新实现。"); - } + async public virtual Task> InsertAsync(IEnumerable entitys) { + await AddRangeAsync(entitys); + return entitys.ToList(); } public int Update(TEntity entity) => OrmUpdate(entity).ExecuteAffrows(); @@ -129,6 +98,178 @@ namespace FreeSql { } protected void ApplyDataFilter(string name, Expression> exp) => DataFilter.Apply(name, exp); + + #region 参考 FreeSql.DbContext/dbset + + TableInfo _tablePriv; + TableInfo _table => _tablePriv ?? (_tablePriv = _fsql.CodeFirst.GetTableByEntity(EntityType)); + ColumnInfo[] _tableIdentitysPriv; + ColumnInfo[] _tableIdentitys => _tableIdentitysPriv ?? (_tableIdentitysPriv = _table.Primarys.Where(a => a.Attribute.IsIdentity).ToArray()); + + bool CanAdd(TEntity[] data, bool isThrow) { + if (data == null) { + if (isThrow) throw new ArgumentNullException(nameof(data)); + return false; + } + foreach (var s in data) if (CanAdd(s, isThrow) == false) return false; + return true; + } + bool CanAdd(IEnumerable data, bool isThrow) { + if (data == null) { + if (isThrow) throw new ArgumentNullException(nameof(data)); + return false; + } + foreach (var s in data) if (CanAdd(s, isThrow) == false) return false; + return true; + } + bool CanAdd(TEntity data, bool isThrow) { + if (data == null) { + if (isThrow) throw new ArgumentNullException(nameof(data)); + return false; + } + var key = _fsql.GetEntityKeyString(data); + if (string.IsNullOrEmpty(key)) { + switch (_fsql.Ado.DataType) { + case DataType.SqlServer: + case DataType.PostgreSQL: + return true; + case DataType.MySql: + case DataType.Oracle: + case DataType.Sqlite: + if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) { + return true; + } + if (isThrow) throw new Exception($"不可添加,未设置主键的值:{_fsql.GetEntityString(data)}"); + return false; + } + } else { + var idval = _fsql.GetEntityIdentityValueWithPrimary(data); + if (idval > 0) { + if (isThrow) throw new Exception($"不可添加,自增属性有值:{_fsql.GetEntityString(data)}"); + return false; + } + } + return true; + } + + void AddPriv(TEntity data, bool isCheck) { + if (isCheck && CanAdd(data, true) == false) return; + if (_tableIdentitys.Length > 0) { + //有自增,马上执行 + switch (_fsql.Ado.DataType) { + case DataType.SqlServer: + case DataType.PostgreSQL: + if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) { + var idtval = this.OrmInsert(data).ExecuteIdentity(); + _fsql.SetEntityIdentityValueWithPrimary(data, idtval); + } else { + var newval = this.OrmInsert(data).ExecuteInserted().First(); + _fsql.MapEntityValue(newval, data); + } + return; + case DataType.MySql: + case DataType.Oracle: + case DataType.Sqlite: + if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) { + var idtval = this.OrmInsert(data).ExecuteIdentity(); + _fsql.SetEntityIdentityValueWithPrimary(data, idtval); + } + return; + } + } else { + this.OrmInsert(data).ExecuteAffrows(); + } + } + public void Add(TEntity source) => AddPriv(source, true); + public void AddRange(TEntity[] data) => AddRange(data.ToList()); + public void AddRange(IEnumerable data) { + if (CanAdd(data, true) == false) return; + if (data.ElementAtOrDefault(1) == default(TEntity)) { + Add(data.First()); + return; + } + if (_tableIdentitys.Length > 0) { + //有自增,马上执行 + switch (_fsql.Ado.DataType) { + case DataType.SqlServer: + case DataType.PostgreSQL: + var rets = this.OrmInsert(data).ExecuteInserted(); + if (rets.Count != data.Count()) throw new Exception($"特别错误:批量添加失败,{_fsql.Ado.DataType} 的返回数据,与添加的数目不匹配"); + var idx = 0; + foreach (var s in data) + _fsql.MapEntityValue(rets[idx++], s); + return; + case DataType.MySql: + case DataType.Oracle: + case DataType.Sqlite: + foreach (var s in data) + AddPriv(s, false); + return; + } + } else { + this.OrmInsert(data).ExecuteAffrows(); + } + } + + async Task AddPrivAsync(TEntity data, bool isCheck) { + if (isCheck && CanAdd(data, true) == false) return; + if (_tableIdentitys.Length > 0) { + //有自增,马上执行 + switch (_fsql.Ado.DataType) { + case DataType.SqlServer: + case DataType.PostgreSQL: + if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) { + var idtval = await this.OrmInsert(data).ExecuteIdentityAsync(); + _fsql.SetEntityIdentityValueWithPrimary(data, idtval); + } else { + var newval = (await this.OrmInsert(data).ExecuteInsertedAsync()).First(); + _fsql.MapEntityValue(newval, data); + } + return; + case DataType.MySql: + case DataType.Oracle: + case DataType.Sqlite: + if (_tableIdentitys.Length == 1 && _table.Primarys.Length == 1) { + var idtval = await this.OrmInsert(data).ExecuteIdentityAsync(); + _fsql.SetEntityIdentityValueWithPrimary(data, idtval); + } + return; + } + } else { + await this.OrmInsert(data).ExecuteAffrowsAsync(); + } + } + public Task AddAsync(TEntity source) => AddPrivAsync(source, true); + public Task AddRangeAsync(TEntity[] data) => AddRangeAsync(data.ToList()); + async public Task AddRangeAsync(IEnumerable data) { + if (CanAdd(data, true) == false) return; + if (data.ElementAtOrDefault(1) == default(TEntity)) { + Add(data.First()); + return; + } + if (_tableIdentitys.Length > 0) { + //有自增,马上执行 + switch (_fsql.Ado.DataType) { + case DataType.SqlServer: + case DataType.PostgreSQL: + 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.MapEntityValue(rets[idx++], s); + return; + case DataType.MySql: + case DataType.Oracle: + case DataType.Sqlite: + foreach (var s in data) + await AddPrivAsync(s, false); + return; + } + } else { + await this.OrmInsert(data).ExecuteAffrowsAsync(); + } + } + #endregion } public abstract class BaseRepository : BaseRepository, IRepository diff --git a/FreeSql/DataAnnotations/ColumnAttribute.cs b/FreeSql/DataAnnotations/ColumnAttribute.cs index dadde641..d58046d2 100644 --- a/FreeSql/DataAnnotations/ColumnAttribute.cs +++ b/FreeSql/DataAnnotations/ColumnAttribute.cs @@ -16,7 +16,7 @@ namespace FreeSql.DataAnnotations { /// public string DbType { get; set; } - internal bool? _IsPrimary, _IsIdentity, _IsNullable, _IsIgnore; + internal bool? _IsPrimary, _IsIdentity, _IsNullable, _IsIgnore, _IsVersion; /// /// 主键 /// @@ -33,6 +33,10 @@ namespace FreeSql.DataAnnotations { /// 忽略此列,不迁移、不插入 /// public bool IsIgnore { get => _IsIgnore ?? false; set => _IsIgnore = value; } + /// + /// 设置行锁(乐观锁)版本号,每次更新累加版本号,若更新整个实体时会附带当前的版本号判断(修改失败时抛出异常) + /// + public bool IsVersion { get => _IsVersion ?? false; set => _IsVersion = value; } /// /// 数据库默认值 diff --git a/FreeSql.DbContext/Extenssions/EntityUtil.cs b/FreeSql/Extensions/EntityUtilExtensions.cs similarity index 89% rename from FreeSql.DbContext/Extenssions/EntityUtil.cs rename to FreeSql/Extensions/EntityUtilExtensions.cs index 0eebeb83..f9477512 100644 --- a/FreeSql.DbContext/Extenssions/EntityUtil.cs +++ b/FreeSql/Extensions/EntityUtilExtensions.cs @@ -6,8 +6,8 @@ using System.Linq.Expressions; using System.Reflection; using System.Text; -namespace FreeSql.Extensions { - public static class EntityUtilFreeSqlExtensions { +namespace FreeSql.Extensions.EntityUtil { + public static class EntityUtilExtensions { static MethodInfo MethodStringBuilderAppend = typeof(StringBuilder).GetMethod("Append", new Type[] { typeof(object) }); static MethodInfo MethodStringBuilderToString = typeof(StringBuilder).GetMethod("ToString", new Type[0]); @@ -66,6 +66,42 @@ namespace FreeSql.Extensions { }); return func(item); } + static ConcurrentDictionary>> _dicGetEntityKeyValues = new ConcurrentDictionary>>(); + /// + /// 获取实体的主键值,多个主键返回数组 + /// + /// + /// + /// + /// + public static object[] GetEntityKeyValues(this IFreeSql orm, TEntity item) { + var func = _dicGetEntityKeyValues.GetOrAdd(orm.Ado.DataType, dt => new ConcurrentDictionary>()).GetOrAdd(typeof(TEntity), t => { + var _table = orm.CodeFirst.GetTableByEntity(t); + var pks = _table.Primarys; + var returnTarget = Expression.Label(typeof(object[])); + var parm1 = Expression.Parameter(typeof(object)); + var var1Parm = Expression.Variable(t); + var var2Ret = Expression.Variable(typeof(object[])); + var exps = new List(new Expression[] { + Expression.Assign(var1Parm, Expression.TypeAs(parm1, t)), + Expression.Assign(var2Ret, Expression.NewArrayBounds(typeof(object), Expression.Constant(pks.Length))), + }); + for (var a = 0; a < pks.Length; a++) { + exps.Add( + Expression.Assign( + Expression.ArrayAccess(var2Ret, Expression.Constant(a)), + Expression.Convert(Expression.MakeMemberAccess(var1Parm, _table.Properties[pks[a].CsName]), typeof(object)) + ) + ); + } + exps.AddRange(new Expression[] { + Expression.Return(returnTarget, var2Ret), + Expression.Label(returnTarget, Expression.Default(typeof(object[]))) + }); + return Expression.Lambda>(Expression.Block(new[] { var1Parm, var2Ret }, exps), new[] { parm1 }).Compile(); + }); + return func(item); + } static ConcurrentDictionary>> _dicGetEntityString = new ConcurrentDictionary>>(); /// /// 获取实体的所有数据,以 (1, 2, xxx) 的形式 @@ -205,9 +241,9 @@ namespace FreeSql.Extensions { Expression.Default(idts0.CsType) ), Expression.Return( - returnTarget, + returnTarget, FreeSql.Internal.Utils.GetDataReaderValueBlockExpression( - typeof(long), + typeof(long), Expression.Convert(Expression.MakeMemberAccess(var1Parm, _table.Properties[idts0.CsName]), typeof(object)) ) ) @@ -262,7 +298,7 @@ namespace FreeSql.Extensions { /// /// public static string[] CompareEntityValueReturnColumns(this IFreeSql orm, TEntity up, TEntity oldval, bool isEqual) { - var func = _dicCompareEntityValueReturnColumns.GetOrAdd(orm.Ado.DataType, dt => new ConcurrentDictionary>()).GetOrAdd(typeof(TEntity), t => { + var func = _dicCompareEntityValueReturnColumns.GetOrAdd(orm.Ado.DataType, dt => new ConcurrentDictionary>()).GetOrAdd(typeof(TEntity), t => { var _table = orm.CodeFirst.GetTableByEntity(t); var returnTarget = Expression.Label(typeof(string[])); var parm1 = Expression.Parameter(typeof(object)); diff --git a/FreeSql/Interface/Curd/IUpdate.cs b/FreeSql/Interface/Curd/IUpdate.cs index 9daca4ab..6c477c51 100644 --- a/FreeSql/Interface/Curd/IUpdate.cs +++ b/FreeSql/Interface/Curd/IUpdate.cs @@ -101,13 +101,6 @@ namespace FreeSql { /// 不存在 /// IUpdate WhereExists(ISelect select, bool notExists = false) where TEntity2 : class; - /// - /// 用于批量修改时,生成 where dbName = case when id = 1 then v1 end 的条件 - /// - /// 属性名 - /// - /// - IUpdate WhereCaseSource(string CsName, Func thenValue); /// /// 设置表名规则,可用于分库/分表,参数1:默认表名;返回值:新表名; diff --git a/FreeSql/Internal/CommonProvider/InsertProvider.cs b/FreeSql/Internal/CommonProvider/InsertProvider.cs index 7b9dc92f..f24006b8 100644 --- a/FreeSql/Internal/CommonProvider/InsertProvider.cs +++ b/FreeSql/Internal/CommonProvider/InsertProvider.cs @@ -95,10 +95,16 @@ namespace FreeSql.Internal.CommonProvider { } internal int SplitExecuteAffrows(int valuesLimit, int parameterLimit) { var ss = SplitSource(valuesLimit, parameterLimit); - if (ss.Any() == false) return 0; - if (ss.Length == 1) return this.RawExecuteAffrows(); - var ret = 0; + if (ss.Any() == false) { + ClearData(); + return ret; + } + if (ss.Length == 1) { + ret = this.RawExecuteAffrows(); + ClearData(); + return ret; + } if (_transaction != null) { for (var a = 0; a < ss.Length; a++) { _source = ss[a]; @@ -120,14 +126,21 @@ namespace FreeSql.Internal.CommonProvider { _transaction = null; } } + ClearData(); return ret; } async internal Task SplitExecuteAffrowsAsync(int valuesLimit, int parameterLimit) { var ss = SplitSource(valuesLimit, parameterLimit); - if (ss.Any() == false) return 0; - if (ss.Length == 1) return await this.RawExecuteAffrowsAsync(); - var ret = 0; + if (ss.Any() == false) { + ClearData(); + return ret; + } + if (ss.Length == 1) { + ret = await this.RawExecuteAffrowsAsync(); + ClearData(); + return ret; + } if (_transaction != null) { for (var a = 0; a < ss.Length; a++) { _source = ss[a]; @@ -149,14 +162,21 @@ namespace FreeSql.Internal.CommonProvider { _transaction = null; } } + ClearData(); return ret; } internal long SplitExecuteIdentity(int valuesLimit, int parameterLimit) { var ss = SplitSource(valuesLimit, parameterLimit); - if (ss.Any() == false) return 0; - if (ss.Length == 1) return this.RawExecuteIdentity(); - long ret = 0; + if (ss.Any() == false) { + ClearData(); + return ret; + } + if (ss.Length == 1) { + ret = this.RawExecuteIdentity(); + ClearData(); + return ret; + } if (_transaction != null) { for (var a = 0; a < ss.Length; a++) { _source = ss[a]; @@ -180,14 +200,21 @@ namespace FreeSql.Internal.CommonProvider { _transaction = null; } } + ClearData(); return ret; } async internal Task SplitExecuteIdentityAsync(int valuesLimit, int parameterLimit) { var ss = SplitSource(valuesLimit, parameterLimit); - if (ss.Any() == false) return 0; - if (ss.Length == 1) return await this.RawExecuteIdentityAsync(); - long ret = 0; + if (ss.Any() == false) { + ClearData(); + return ret; + } + if (ss.Length == 1) { + ret = await this.RawExecuteIdentityAsync(); + ClearData(); + return ret; + } if (_transaction != null) { for (var a = 0; a < ss.Length; a++) { _source = ss[a]; @@ -211,14 +238,21 @@ namespace FreeSql.Internal.CommonProvider { _transaction = null; } } + ClearData(); return ret; } internal List SplitExecuteInserted(int valuesLimit, int parameterLimit) { var ss = SplitSource(valuesLimit, parameterLimit); - if (ss.Any() == false) return new List(); - if (ss.Length == 1) return this.RawExecuteInserted(); - var ret = new List(); + if (ss.Any() == false) { + ClearData(); + return ret; + } + if (ss.Length == 1) { + ret = this.RawExecuteInserted(); + ClearData(); + return ret; + } if (_transaction != null) { for (var a = 0; a < ss.Length; a++) { _source = ss[a]; @@ -240,14 +274,21 @@ namespace FreeSql.Internal.CommonProvider { _transaction = null; } } + ClearData(); return ret; } async internal Task> SplitExecuteInsertedAsync(int valuesLimit, int parameterLimit) { var ss = SplitSource(valuesLimit, parameterLimit); - if (ss.Any() == false) return new List(); - if (ss.Length == 1) return await this.RawExecuteInsertedAsync(); - var ret = new List(); + if (ss.Any() == false) { + ClearData(); + return ret; + } + if (ss.Length == 1) { + ret = await this.RawExecuteInsertedAsync(); + ClearData(); + return ret; + } if (_transaction != null) { for (var a = 0; a < ss.Length; a++) { _source = ss[a]; @@ -269,27 +310,20 @@ namespace FreeSql.Internal.CommonProvider { _transaction = null; } } + ClearData(); return ret; } #endregion - internal int RawExecuteAffrows() { - var affrows = _orm.Ado.ExecuteNonQuery(_transaction, CommandType.Text, ToSql(), _params); - this.ClearData(); - return affrows; - } - async internal Task RawExecuteAffrowsAsync() { - var affrows = await _orm.Ado.ExecuteNonQueryAsync(_transaction, CommandType.Text, ToSql(), _params); - this.ClearData(); - return affrows; - } + internal int RawExecuteAffrows() => _orm.Ado.ExecuteNonQuery(_transaction, CommandType.Text, ToSql(), _params); + internal Task RawExecuteAffrowsAsync() => _orm.Ado.ExecuteNonQueryAsync(_transaction, CommandType.Text, ToSql(), _params); internal abstract long RawExecuteIdentity(); internal abstract Task RawExecuteIdentityAsync(); internal abstract List RawExecuteInserted(); internal abstract Task> RawExecuteInsertedAsync(); - public virtual int ExecuteAffrows() => RawExecuteAffrows(); - public virtual Task ExecuteAffrowsAsync() => RawExecuteAffrowsAsync(); + public abstract int ExecuteAffrows(); + public abstract Task ExecuteAffrowsAsync(); public abstract long ExecuteIdentity(); public abstract Task ExecuteIdentityAsync(); public abstract List ExecuteInserted(); diff --git a/FreeSql/Internal/CommonProvider/UpdateProvider.cs b/FreeSql/Internal/CommonProvider/UpdateProvider.cs index a3758662..12ccb98a 100644 --- a/FreeSql/Internal/CommonProvider/UpdateProvider.cs +++ b/FreeSql/Internal/CommonProvider/UpdateProvider.cs @@ -1,4 +1,5 @@ -using FreeSql.Internal.Model; +using FreeSql.Extensions.EntityUtil; +using FreeSql.Internal.Model; using System; using System.Collections.Generic; using System.Data; @@ -55,20 +56,197 @@ namespace FreeSql.Internal.CommonProvider { return this; } - public int ExecuteAffrows() { + protected void ValidateVersionAndThrow(int affrows) { + if (_table.VersionColumn != null && _source.Count > 0) { + if (affrows != _source.Count) + throw new Exception($"记录可能不存在,或者【行级乐观锁】版本过旧,更新数量{_source.Count},影响的行数{affrows}。"); + foreach (var d in _source) + _orm.SetEntityIncrByWithPropertyName(d, _table.VersionColumn.CsName, 1); + } + } + + #region 参数化数据限制,或values数量限制 + internal List[] SplitSource(int valuesLimit, int parameterLimit) { + valuesLimit = valuesLimit - 1; + parameterLimit = parameterLimit - 1; + if (_source == null || _source.Any() == false) return new List[0]; + if (_source.Count == 1) return new[] { _source }; + if (_noneParameter) { + if (_source.Count < valuesLimit) return new[] { _source }; + + var execCount = (int)Math.Ceiling(1.0 * _source.Count / valuesLimit); + var ret = new List[execCount]; + for (var a = 0; a < execCount; a++) { + var subSource = new List(); + subSource = _source.GetRange(a * valuesLimit, Math.Min(valuesLimit, _source.Count - a * valuesLimit)); + ret[a] = subSource; + } + return ret; + } else { + var colSum = _table.Columns.Count - _ignore.Count; + var takeMax = parameterLimit / colSum; + var pamTotal = colSum * _source.Count; + if (pamTotal < parameterLimit) return new[] { _source }; + + var execCount = (int)Math.Ceiling(1.0 * pamTotal / parameterLimit); + var ret = new List[execCount]; + for (var a = 0; a < execCount; a++) { + var subSource = new List(); + subSource = _source.GetRange(a * takeMax, Math.Min(takeMax, _source.Count - a * takeMax)); + ret[a] = subSource; + } + return ret; + } + } + internal int SplitExecuteAffrows(int valuesLimit, int parameterLimit) { + var ss = SplitSource(valuesLimit, parameterLimit); + var ret = 0; + if (ss.Length <= 1) { + ret = this.RawExecuteAffrows(); + ClearData(); + return ret; + } + if (_transaction != null) { + for (var a = 0; a < ss.Length; a++) { + _source = ss[a]; + ret += this.RawExecuteAffrows(); + } + } else { + using (var conn = _orm.Ado.MasterPool.Get()) { + _transaction = conn.Value.BeginTransaction(); + try { + for (var a = 0; a < ss.Length; a++) { + _source = ss[a]; + ret += this.RawExecuteAffrows(); + } + _transaction.Commit(); + } catch { + _transaction.Rollback(); + throw; + } + _transaction = null; + } + } + ClearData(); + return ret; + } + async internal Task SplitExecuteAffrowsAsync(int valuesLimit, int parameterLimit) { + var ss = SplitSource(valuesLimit, parameterLimit); + var ret = 0; + if (ss.Length <= 1) { + ret = await this.RawExecuteAffrowsAsync(); + ClearData(); + return ret; + } + if (_transaction != null) { + for (var a = 0; a < ss.Length; a++) { + _source = ss[a]; + ret += await this.RawExecuteAffrowsAsync(); + } + } else { + using (var conn = await _orm.Ado.MasterPool.GetAsync()) { + _transaction = conn.Value.BeginTransaction(); + try { + for (var a = 0; a < ss.Length; a++) { + _source = ss[a]; + ret += await this.RawExecuteAffrowsAsync(); + } + _transaction.Commit(); + } catch { + _transaction.Rollback(); + throw; + } + _transaction = null; + } + } + ClearData(); + return ret; + } + internal List SplitExecuteUpdated(int valuesLimit, int parameterLimit) { + var ss = SplitSource(valuesLimit, parameterLimit); + var ret = new List(); + if (ss.Length <= 1) { + ret = this.RawExecuteUpdated(); + ClearData(); + return ret; + } + if (_transaction != null) { + for (var a = 0; a < ss.Length; a++) { + _source = ss[a]; + ret.AddRange(this.RawExecuteUpdated()); + } + } else { + using (var conn = _orm.Ado.MasterPool.Get()) { + _transaction = conn.Value.BeginTransaction(); + try { + for (var a = 0; a < ss.Length; a++) { + _source = ss[a]; + ret.AddRange(this.RawExecuteUpdated()); + } + _transaction.Commit(); + } catch { + _transaction.Rollback(); + throw; + } + _transaction = null; + } + } + ClearData(); + return ret; + } + async internal Task> SplitExecuteUpdatedAsync(int valuesLimit, int parameterLimit) { + var ss = SplitSource(valuesLimit, parameterLimit); + var ret = new List(); + if (ss.Length <= 1) { + ret = await this.RawExecuteUpdatedAsync(); + ClearData(); + return ret; + } + if (_transaction != null) { + for (var a = 0; a < ss.Length; a++) { + _source = ss[a]; + ret.AddRange(await this.RawExecuteUpdatedAsync()); + } + } else { + using (var conn = await _orm.Ado.MasterPool.GetAsync()) { + _transaction = conn.Value.BeginTransaction(); + try { + for (var a = 0; a < ss.Length; a++) { + _source = ss[a]; + ret.AddRange(await this.RawExecuteUpdatedAsync()); + } + _transaction.Commit(); + } catch { + _transaction.Rollback(); + throw; + } + _transaction = null; + } + } + ClearData(); + return ret; + } + #endregion + + internal int RawExecuteAffrows() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return 0; var affrows = _orm.Ado.ExecuteNonQuery(_transaction, CommandType.Text, sql, _params.Concat(_paramsSource).ToArray()); - this.ClearData(); + ValidateVersionAndThrow(affrows); return affrows; } - async public Task ExecuteAffrowsAsync() { + async internal Task RawExecuteAffrowsAsync() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return 0; var affrows = await _orm.Ado.ExecuteNonQueryAsync(_transaction, CommandType.Text, sql, _params.Concat(_paramsSource).ToArray()); - this.ClearData(); + ValidateVersionAndThrow(affrows); return affrows; } + internal abstract List RawExecuteUpdated(); + internal abstract Task> RawExecuteUpdatedAsync(); + + public abstract int ExecuteAffrows(); + public abstract Task ExecuteAffrowsAsync(); public abstract List ExecuteUpdated(); public abstract Task> ExecuteUpdatedAsync(); @@ -88,7 +266,7 @@ namespace FreeSql.Internal.CommonProvider { public IUpdate SetSource(IEnumerable source) { if (source == null || source.Any() == false) return this; _source.AddRange(source.Where(a => a != null)); - return this.Where(_source); + return this; } public IUpdate Set(Expression> column, TMember value) { @@ -144,12 +322,12 @@ namespace FreeSql.Internal.CommonProvider { public IUpdate Where(IEnumerable items) => this.Where(_commonUtils.WhereItems(_table, "", items)); public IUpdate WhereExists(ISelect select, bool notExists = false) where TEntity2 : class => this.Where($"{(notExists ? "NOT " : "")}EXISTS({select.ToSql("1")})"); - public IUpdate WhereCaseSource(string CsName, Func thenValue) { - if (_source.Any() == false) return this; + protected string WhereCaseSource(string CsName, Func thenValue) { + if (_source.Any() == false) return null; if (_table.ColumnsByCs.ContainsKey(CsName) == false) throw new Exception($"找不到 {CsName} 对应的列"); if (thenValue == null) throw new ArgumentNullException("thenValue 参数不可为 null"); - if (_source.Count == 0) return this; + if (_source.Count == 0) return null; if (_source.Count == 1) { var col = _table.ColumnsByCs[CsName]; @@ -159,7 +337,7 @@ namespace FreeSql.Internal.CommonProvider { var value = _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(_source.First()) : DBNull.Value; sb.Append(thenValue(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.CsType, value))); - return this.Where(sb.ToString()); + return sb.ToString(); } else { var caseWhen = new StringBuilder(); @@ -169,17 +347,24 @@ namespace FreeSql.Internal.CommonProvider { var col = _table.ColumnsByCs[CsName]; var sb = new StringBuilder(); - sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = ").Append(cw); - foreach (var d in _source) { - sb.Append(" \r\nWHEN "); - ToSqlWhen(sb, _table.Primarys, d); - sb.Append(" THEN "); - var value = _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(d) : DBNull.Value; - sb.Append(thenValue(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.CsType, value))); - } - sb.Append(" END"); + sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = "); - return this.Where(sb.ToString()); + var isnull = false; + var cwsb = new StringBuilder().Append(cw); + foreach (var d in _source) { + cwsb.Append(" \r\nWHEN "); + ToSqlWhen(cwsb, _table.Primarys, d); + cwsb.Append(" THEN "); + var value = _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(d) : DBNull.Value; + cwsb.Append(thenValue(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.CsType, value))); + if (isnull == false) isnull = value == null || value == DBNull.Value; + } + cwsb.Append(" END"); + if (isnull == false) sb.Append(cwsb.ToString()); + else sb.Append("NULL"); + cwsb.Clear(); + + return sb.ToString(); } } @@ -191,7 +376,7 @@ namespace FreeSql.Internal.CommonProvider { return this; } public string ToSql() { - if (_where.Length == 0) return null; + if (_where.Length == 0 && _source.Any() == false) return null; var sb = new StringBuilder(); sb.Append("UPDATE ").Append(_commonUtils.QuoteSqlName(_tableRule?.Invoke(_table.DbName) ?? _table.DbName)).Append(" SET "); @@ -224,14 +409,6 @@ namespace FreeSql.Internal.CommonProvider { var caseWhen = new StringBuilder(); caseWhen.Append("CASE "); ToSqlCase(caseWhen, _table.Primarys); - //if (_table.Primarys.Length > 1) caseWhen.Append("CONCAT("); - //var pkidx = 0; - //foreach (var pk in _table.Primarys) { - // if (pkidx > 0) caseWhen.Append(", "); - // caseWhen.Append(_commonUtils.QuoteSqlName(pk.Attribute.Name)); - // ++pkidx; - //} - //if (_table.Primarys.Length > 1) caseWhen.Append(")"); var cw = caseWhen.ToString(); _paramsSource.Clear(); @@ -239,37 +416,56 @@ namespace FreeSql.Internal.CommonProvider { foreach (var col in _table.Columns.Values) { if (col.Attribute.IsIdentity == false && _ignore.ContainsKey(col.CsName) == false) { if (colidx > 0) sb.Append(", "); - sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = ").Append(cw); + sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = "); + + var isnull = false; + var cwsb = new StringBuilder().Append(cw); foreach (var d in _source) { - sb.Append(" \r\nWHEN "); - ToSqlWhen(sb, _table.Primarys, d); - //if (_table.Primarys.Length > 1) sb.Append("CONCAT("); - //pkidx = 0; - //foreach (var pk in _table.Primarys) { - // if (pkidx > 0) sb.Append(", "); - // sb.Append(_commonUtils.FormatSql("{0}", _table.Properties.TryGetValue(pk.CsName, out var tryp2) ? tryp2.GetValue(d) : null)); - // ++pkidx; - //} - //if (_table.Primarys.Length > 1) sb.Append(")"); - sb.Append(" THEN "); + cwsb.Append(" \r\nWHEN "); + ToSqlWhen(cwsb, _table.Primarys, d); + cwsb.Append(" THEN "); var value = _table.Properties.TryGetValue(col.CsName, out var tryp) ? tryp.GetValue(d) : DBNull.Value; if (_noneParameter) { - sb.Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.CsType, value)); + cwsb.Append(_commonUtils.GetNoneParamaterSqlValue(_paramsSource, col.CsType, value)); } else { - sb.Append(_commonUtils.QuoteWriteParamter(col.CsType, _commonUtils.QuoteParamterName($"p_{_paramsSource.Count}"))); + cwsb.Append(_commonUtils.QuoteWriteParamter(col.CsType, _commonUtils.QuoteParamterName($"p_{_paramsSource.Count}"))); _commonUtils.AppendParamter(_paramsSource, null, col.CsType, value); } + if (isnull == false) isnull = value == null || value == DBNull.Value; } - sb.Append(" END"); + cwsb.Append(" END"); + if (isnull == false) sb.Append(cwsb.ToString()); + else sb.Append("NULL"); + cwsb.Clear(); + ++colidx; } } if (colidx == 0) return null; - } else + } else if (_setIncr.Length == 0) return null; - sb.Append(_setIncr.ToString()); - sb.Append(" \r\nWHERE ").Append(_where.ToString().Substring(5)); + if (_setIncr.Length > 0) + sb.Append(_set.Length > 0 ? _setIncr.ToString() : _setIncr.ToString().Substring(2)); + + if (_table.VersionColumn != null) { + var vcname = _commonUtils.QuoteSqlName(_table.VersionColumn.Attribute.Name); + sb.Append(", ").Append(vcname).Append(" = ").Append(vcname).Append(" + 1"); + } + + sb.Append(" \r\nWHERE "); + if (_source.Any()) + sb.Append("(").Append(_commonUtils.WhereItems(_table, "", _source)).Append(")"); + + if (_where.Length > 0) + sb.Append(_source.Any() ? _where.ToString() : _where.ToString().Substring(5)); + + if (_table.VersionColumn != null) { + var versionCondi = WhereCaseSource(_table.VersionColumn.CsName, sqlval => sqlval); + if (string.IsNullOrEmpty(versionCondi) == false) + sb.Append(" AND ").Append(versionCondi); + } + return sb.ToString(); } } diff --git a/FreeSql/Internal/CommonUtils.cs b/FreeSql/Internal/CommonUtils.cs index f90a61f4..1294997a 100644 --- a/FreeSql/Internal/CommonUtils.cs +++ b/FreeSql/Internal/CommonUtils.cs @@ -1,5 +1,6 @@ using FreeSql.DataAnnotations; using FreeSql.DatabaseModel; +using FreeSql.Extensions.EntityUtil; using FreeSql.Internal.Model; using System; using System.Collections; @@ -79,6 +80,7 @@ namespace FreeSql.Internal { if (attr._IsIdentity == null) attr._IsIdentity = trycol.IsIdentity; if (attr._IsNullable == null) attr._IsNullable = trycol.IsNullable; if (attr._IsIgnore == null) attr._IsIgnore = trycol.IsIgnore; + if (attr._IsVersion == null) attr._IsVersion = trycol.IsVersion; if (attr.DbDefautValue == null) attr.DbDefautValue = trycol.DbDefautValue; return attr; } @@ -136,7 +138,8 @@ namespace FreeSql.Internal { if (table.Primarys.Length == 1) { var sbin = new StringBuilder(); sbin.Append(aliasAndDot).Append(this.QuoteSqlName(table.Primarys.First().Attribute.Name)); - var indt = its.Select(a => table.Properties.TryGetValue(table.Primarys.First().CsName, out var trycol) ? this.FormatSql("{0}", trycol.GetValue(a)) : null).Where(a => a != null).ToArray(); + var indt = its.Select(a => /*this.FormatSql("{0}", _orm.GetEntityKeyValues(a))*/ + table.Properties.TryGetValue(table.Primarys.First().CsName, out var trycol) ? this.FormatSql("{0}", trycol.GetValue(a)) : null).Where(a => a != null).ToArray(); if (indt.Any() == false) return null; if (indt.Length == 1) sbin.Append(" = ").Append(indt.First()); else sbin.Append(" IN (").Append(string.Join(",", indt)).Append(")"); diff --git a/FreeSql/Internal/Model/TableInfo.cs b/FreeSql/Internal/Model/TableInfo.cs index c7ae1264..87768191 100644 --- a/FreeSql/Internal/Model/TableInfo.cs +++ b/FreeSql/Internal/Model/TableInfo.cs @@ -17,6 +17,7 @@ namespace FreeSql.Internal.Model { public string DbOldName { get; set; } public string SelectFilter { get; set; } + public ColumnInfo VersionColumn { get; set; } ConcurrentDictionary _refs { get; } = new ConcurrentDictionary(StringComparer.CurrentCultureIgnoreCase); diff --git a/FreeSql/Internal/UtilsExpressionTree.cs b/FreeSql/Internal/UtilsExpressionTree.cs index 45b291c3..1eb539f9 100644 --- a/FreeSql/Internal/UtilsExpressionTree.cs +++ b/FreeSql/Internal/UtilsExpressionTree.cs @@ -107,6 +107,11 @@ namespace FreeSql.Internal { trytb.Columns.Add(colattr.Name, col); trytb.ColumnsByCs.Add(p.Name, col); } + trytb.VersionColumn = trytb.Columns.Values.Where(a => a.Attribute.IsVersion == true).LastOrDefault(); + if (trytb.VersionColumn != null) { + if (trytb.VersionColumn.CsType.IsNullableType() || trytb.VersionColumn.CsType.IsNumberType() == false) + throw new Exception($"属性{trytb.VersionColumn.CsName} 被标注为行锁(乐观锁)(IsVersion),但其必须为数字类型,并且不可为 Nullable"); + } trytb.Primarys = trytb.Columns.Values.Where(a => a.Attribute.IsPrimary == true).ToArray(); if (trytb.Primarys.Any() == false) { var identcol = trytb.Columns.Values.Where(a => a.Attribute.IsIdentity == true).FirstOrDefault(); diff --git a/FreeSql/MySql/Curd/MySqlInsert.cs b/FreeSql/MySql/Curd/MySqlInsert.cs index 1b520285..8c2da2ba 100644 --- a/FreeSql/MySql/Curd/MySqlInsert.cs +++ b/FreeSql/MySql/Curd/MySqlInsert.cs @@ -23,17 +23,13 @@ namespace FreeSql.MySql.Curd { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return 0; - var id = long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_transaction, CommandType.Text, string.Concat(sql, "; SELECT LAST_INSERT_ID();"), _params)), out var trylng) ? trylng : 0; - this.ClearData(); - return id; + return long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_transaction, CommandType.Text, string.Concat(sql, "; SELECT LAST_INSERT_ID();"), _params)), out var trylng) ? trylng : 0; } async internal override Task RawExecuteIdentityAsync() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return 0; - var id = long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_transaction, CommandType.Text, string.Concat(sql, "; SELECT LAST_INSERT_ID();"), _params)), out var trylng) ? trylng : 0; - this.ClearData(); - return id; + return long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_transaction, CommandType.Text, string.Concat(sql, "; SELECT LAST_INSERT_ID();"), _params)), out var trylng) ? trylng : 0; } internal override List RawExecuteInserted() { var sql = this.ToSql(); @@ -48,9 +44,7 @@ namespace FreeSql.MySql.Curd { sb.Append(_commonUtils.QuoteReadColumn(col.CsType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); ++colidx; } - var ret = _orm.Ado.Query(_transaction, CommandType.Text, sb.ToString(), _params); - this.ClearData(); - return ret; + return _orm.Ado.Query(_transaction, CommandType.Text, sb.ToString(), _params); } async internal override Task> RawExecuteInsertedAsync() { var sql = this.ToSql(); @@ -65,9 +59,7 @@ namespace FreeSql.MySql.Curd { sb.Append(_commonUtils.QuoteReadColumn(col.CsType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); ++colidx; } - var ret = await _orm.Ado.QueryAsync(_transaction, CommandType.Text, sb.ToString(), _params); - this.ClearData(); - return ret; + return await _orm.Ado.QueryAsync(_transaction, CommandType.Text, sb.ToString(), _params); } } } diff --git a/FreeSql/MySql/Curd/MySqlUpdate.cs b/FreeSql/MySql/Curd/MySqlUpdate.cs index a647a68d..d3e8329c 100644 --- a/FreeSql/MySql/Curd/MySqlUpdate.cs +++ b/FreeSql/MySql/Curd/MySqlUpdate.cs @@ -14,7 +14,13 @@ namespace FreeSql.MySql.Curd { : base(orm, commonUtils, commonExpression, dywhere) { } - public override List ExecuteUpdated() { + public override int ExecuteAffrows() => base.SplitExecuteAffrows(500, 3000); + public override Task ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(500, 3000); + public override List ExecuteUpdated() => base.SplitExecuteUpdated(500, 3000); + public override Task> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(500, 3000); + + + internal override List RawExecuteUpdated() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return new List(); @@ -28,10 +34,10 @@ namespace FreeSql.MySql.Curd { ++colidx; } var ret = _orm.Ado.Query(_transaction, CommandType.Text, sb.ToString(), _params.Concat(_paramsSource).ToArray()); - this.ClearData(); + ValidateVersionAndThrow(ret.Count); return ret; } - async public override Task> ExecuteUpdatedAsync() { + async internal override Task> RawExecuteUpdatedAsync() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return new List(); @@ -45,7 +51,7 @@ namespace FreeSql.MySql.Curd { ++colidx; } var ret = await _orm.Ado.QueryAsync(_transaction, CommandType.Text, sb.ToString(), _params.Concat(_paramsSource).ToArray()); - this.ClearData(); + ValidateVersionAndThrow(ret.Count); return ret; } diff --git a/FreeSql/Oracle/Curd/OracleInsert.cs b/FreeSql/Oracle/Curd/OracleInsert.cs index cc69deb6..33f243f8 100644 --- a/FreeSql/Oracle/Curd/OracleInsert.cs +++ b/FreeSql/Oracle/Curd/OracleInsert.cs @@ -95,9 +95,7 @@ namespace FreeSql.Oracle.Curd { var identParam = _commonUtils.AppendParamter(null, $"{_identCol.CsName}99", _identCol.CsType, 0) as OracleParameter; identParam.Direction = ParameterDirection.Output; _orm.Ado.ExecuteNonQuery(_transaction, CommandType.Text, $"{sql} RETURNING {identColName} INTO {identParam.ParameterName}", _params.Concat(new[] { identParam }).ToArray()); - var id = long.TryParse(string.Concat(identParam.Value), out var trylng) ? trylng : 0; - this.ClearData(); - return id; + return long.TryParse(string.Concat(identParam.Value), out var trylng) ? trylng : 0; } async internal override Task RawExecuteIdentityAsync() { var sql = this.ToSql(); @@ -111,25 +109,21 @@ namespace FreeSql.Oracle.Curd { var identParam = _commonUtils.AppendParamter(null, $"{_identCol.CsName}99", _identCol.CsType, 0) as OracleParameter; identParam.Direction = ParameterDirection.Output; await _orm.Ado.ExecuteNonQueryAsync(_transaction, CommandType.Text, $"{sql} RETURNING {identColName} INTO {identParam.ParameterName}", _params.Concat(new[] { identParam }).ToArray()); - var id = long.TryParse(string.Concat(identParam.Value), out var trylng) ? trylng : 0; - this.ClearData(); - return id; + return long.TryParse(string.Concat(identParam.Value), out var trylng) ? trylng : 0; } internal override List RawExecuteInserted() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return new List(); - this.ExecuteAffrows(); - this.ClearData(); + this.RawExecuteAffrows(); return _source; } async internal override Task> RawExecuteInsertedAsync() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return new List(); - await this.ExecuteAffrowsAsync(); - this.ClearData(); + await this.RawExecuteAffrowsAsync(); return _source; } } diff --git a/FreeSql/Oracle/Curd/OracleUpdate.cs b/FreeSql/Oracle/Curd/OracleUpdate.cs index a7ad6a6a..6f1166f4 100644 --- a/FreeSql/Oracle/Curd/OracleUpdate.cs +++ b/FreeSql/Oracle/Curd/OracleUpdate.cs @@ -15,10 +15,16 @@ namespace FreeSql.Oracle.Curd { : base(orm, commonUtils, commonExpression, dywhere) { } - public override List ExecuteUpdated() { + public override int ExecuteAffrows() => base.SplitExecuteAffrows(200, 999); + public override Task ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(200, 999); + public override List ExecuteUpdated() => base.SplitExecuteUpdated(200, 999); + public override Task> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(200, 999); + + + internal override List RawExecuteUpdated() { throw new NotImplementedException(); } - public override Task> ExecuteUpdatedAsync() { + internal override Task> RawExecuteUpdatedAsync() { throw new NotImplementedException(); } diff --git a/FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs b/FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs index 31222f59..b36e0987 100644 --- a/FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs +++ b/FreeSql/PostgreSQL/Curd/PostgreSQLInsert.cs @@ -29,9 +29,7 @@ namespace FreeSql.PostgreSQL.Curd { _orm.Ado.ExecuteNonQuery(_transaction, CommandType.Text, sql, _params); return 0; } - var id = long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_transaction, CommandType.Text, string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)), _params)), out var trylng) ? trylng : 0; - this.ClearData(); - return id; + return long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_transaction, CommandType.Text, string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)), _params)), out var trylng) ? trylng : 0; } async internal override Task RawExecuteIdentityAsync() { var sql = this.ToSql(); @@ -42,9 +40,7 @@ namespace FreeSql.PostgreSQL.Curd { await _orm.Ado.ExecuteNonQueryAsync(_transaction, CommandType.Text, sql, _params); return 0; } - var id = long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_transaction, CommandType.Text, string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)), _params)), out var trylng) ? trylng : 0; - this.ClearData(); - return id; + return long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_transaction, CommandType.Text, string.Concat(sql, " RETURNING ", _commonUtils.QuoteSqlName(identCols.First().Value.Attribute.Name)), _params)), out var trylng) ? trylng : 0; } internal override List RawExecuteInserted() { @@ -60,9 +56,7 @@ namespace FreeSql.PostgreSQL.Curd { sb.Append(_commonUtils.QuoteReadColumn(col.CsType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); ++colidx; } - var ret = _orm.Ado.Query(_transaction, CommandType.Text, sb.ToString(), _params); - this.ClearData(); - return ret; + return _orm.Ado.Query(_transaction, CommandType.Text, sb.ToString(), _params); } async internal override Task> RawExecuteInsertedAsync() { var sql = this.ToSql(); @@ -77,9 +71,7 @@ namespace FreeSql.PostgreSQL.Curd { sb.Append(_commonUtils.QuoteReadColumn(col.CsType, _commonUtils.QuoteSqlName(col.Attribute.Name))).Append(" as ").Append(_commonUtils.QuoteSqlName(col.CsName)); ++colidx; } - var ret = await _orm.Ado.QueryAsync(_transaction, CommandType.Text, sb.ToString(), _params); - this.ClearData(); - return ret; + return await _orm.Ado.QueryAsync(_transaction, CommandType.Text, sb.ToString(), _params); } } } diff --git a/FreeSql/PostgreSQL/Curd/PostgreSQLUpdate.cs b/FreeSql/PostgreSQL/Curd/PostgreSQLUpdate.cs index b183b173..98515b94 100644 --- a/FreeSql/PostgreSQL/Curd/PostgreSQLUpdate.cs +++ b/FreeSql/PostgreSQL/Curd/PostgreSQLUpdate.cs @@ -14,7 +14,13 @@ namespace FreeSql.PostgreSQL.Curd { : base(orm, commonUtils, commonExpression, dywhere) { } - public override List ExecuteUpdated() { + public override int ExecuteAffrows() => base.SplitExecuteAffrows(500, 3000); + public override Task ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(500, 3000); + public override List ExecuteUpdated() => base.SplitExecuteUpdated(500, 3000); + public override Task> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(500, 3000); + + + internal override List RawExecuteUpdated() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return new List(); @@ -28,10 +34,10 @@ namespace FreeSql.PostgreSQL.Curd { ++colidx; } var ret = _orm.Ado.Query(_transaction, CommandType.Text, sb.ToString(), _params.Concat(_paramsSource).ToArray()); - this.ClearData(); + ValidateVersionAndThrow(ret.Count); return ret; } - async public override Task> ExecuteUpdatedAsync() { + async internal override Task> RawExecuteUpdatedAsync() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return new List(); @@ -45,7 +51,7 @@ namespace FreeSql.PostgreSQL.Curd { ++colidx; } var ret = await _orm.Ado.QueryAsync(_transaction, CommandType.Text, sb.ToString(), _params.Concat(_paramsSource).ToArray()); - this.ClearData(); + ValidateVersionAndThrow(ret.Count); return ret; } diff --git a/FreeSql/SqlServer/Curd/SqlServerInsert.cs b/FreeSql/SqlServer/Curd/SqlServerInsert.cs index 20c197a9..6932bf93 100644 --- a/FreeSql/SqlServer/Curd/SqlServerInsert.cs +++ b/FreeSql/SqlServer/Curd/SqlServerInsert.cs @@ -26,17 +26,13 @@ namespace FreeSql.SqlServer.Curd { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return 0; - var id = long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_transaction, CommandType.Text, string.Concat(sql, "; SELECT SCOPE_IDENTITY();"), _params)), out var trylng) ? trylng : 0; - this.ClearData(); - return id; + return long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_transaction, CommandType.Text, string.Concat(sql, "; SELECT SCOPE_IDENTITY();"), _params)), out var trylng) ? trylng : 0; } async internal override Task RawExecuteIdentityAsync() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return 0; - var id = long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_transaction, CommandType.Text, string.Concat(sql, "; SELECT SCOPE_IDENTITY();"), _params)), out var trylng) ? trylng : 0; - this.ClearData(); - return id; + return long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_transaction, CommandType.Text, string.Concat(sql, "; SELECT SCOPE_IDENTITY();"), _params)), out var trylng) ? trylng : 0; } internal override List RawExecuteInserted() { @@ -57,9 +53,7 @@ namespace FreeSql.SqlServer.Curd { sb.Insert(0, sql.Substring(0, validx + 1)); sb.Append(sql.Substring(validx + 1)); - var ret = _orm.Ado.Query(_transaction, CommandType.Text, sb.ToString(), _params); - this.ClearData(); - return ret; + return _orm.Ado.Query(_transaction, CommandType.Text, sb.ToString(), _params); } async internal override Task> RawExecuteInsertedAsync() { var sql = this.ToSql(); @@ -79,9 +73,7 @@ namespace FreeSql.SqlServer.Curd { sb.Insert(0, sql.Substring(0, validx + 1)); sb.Append(sql.Substring(validx + 1)); - var ret = await _orm.Ado.QueryAsync(_transaction, CommandType.Text, sb.ToString(), _params); - this.ClearData(); - return ret; + return await _orm.Ado.QueryAsync(_transaction, CommandType.Text, sb.ToString(), _params); } } } \ No newline at end of file diff --git a/FreeSql/SqlServer/Curd/SqlServerUpdate.cs b/FreeSql/SqlServer/Curd/SqlServerUpdate.cs index 74075a97..ccc5c09c 100644 --- a/FreeSql/SqlServer/Curd/SqlServerUpdate.cs +++ b/FreeSql/SqlServer/Curd/SqlServerUpdate.cs @@ -15,7 +15,13 @@ namespace FreeSql.SqlServer.Curd { : base(orm, commonUtils, commonExpression, dywhere) { } - public override List ExecuteUpdated() { + public override int ExecuteAffrows() => base.SplitExecuteAffrows(500, 2100); + public override Task ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(500, 2100); + public override List ExecuteUpdated() => base.SplitExecuteUpdated(500, 2100); + public override Task> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(500, 2100); + + + internal override List RawExecuteUpdated() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return new List(); @@ -34,10 +40,10 @@ namespace FreeSql.SqlServer.Curd { sb.Append(sql.Substring(validx)); var ret = _orm.Ado.Query(_transaction, CommandType.Text, sb.ToString(), _params.Concat(_paramsSource).ToArray()); - this.ClearData(); + ValidateVersionAndThrow(ret.Count); return ret; } - async public override Task> ExecuteUpdatedAsync() { + async internal override Task> RawExecuteUpdatedAsync() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return new List(); @@ -56,7 +62,7 @@ namespace FreeSql.SqlServer.Curd { sb.Append(sql.Substring(validx)); var ret = await _orm.Ado.QueryAsync(_transaction, CommandType.Text, sb.ToString(), _params.Concat(_paramsSource).ToArray()); - this.ClearData(); + ValidateVersionAndThrow(ret.Count); return ret; } diff --git a/FreeSql/Sqlite/Curd/SqliteInsert.cs b/FreeSql/Sqlite/Curd/SqliteInsert.cs index 810e794f..7feda22a 100644 --- a/FreeSql/Sqlite/Curd/SqliteInsert.cs +++ b/FreeSql/Sqlite/Curd/SqliteInsert.cs @@ -25,32 +25,26 @@ namespace FreeSql.Sqlite.Curd { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return 0; - var id = long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_transaction, CommandType.Text, string.Concat(sql, "; SELECT last_insert_rowid();"), _params)), out var trylng) ? trylng : 0; - this.ClearData(); - return id; + return long.TryParse(string.Concat(_orm.Ado.ExecuteScalar(_transaction, CommandType.Text, string.Concat(sql, "; SELECT last_insert_rowid();"), _params)), out var trylng) ? trylng : 0; } async internal override Task RawExecuteIdentityAsync() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return 0; - var id = long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_transaction, CommandType.Text, string.Concat(sql, "; SELECT last_insert_rowid();"), _params)), out var trylng) ? trylng : 0; - this.ClearData(); - return id; + return long.TryParse(string.Concat(await _orm.Ado.ExecuteScalarAsync(_transaction, CommandType.Text, string.Concat(sql, "; SELECT last_insert_rowid();"), _params)), out var trylng) ? trylng : 0; } internal override List RawExecuteInserted() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return new List(); - this.ExecuteAffrows(); - this.ClearData(); + this.RawExecuteAffrows(); return _source; } async internal override Task> RawExecuteInsertedAsync() { var sql = this.ToSql(); if (string.IsNullOrEmpty(sql)) return new List(); - await this.ExecuteAffrowsAsync(); - this.ClearData(); + await this.RawExecuteAffrowsAsync(); return _source; } } diff --git a/FreeSql/Sqlite/Curd/SqliteUpdate.cs b/FreeSql/Sqlite/Curd/SqliteUpdate.cs index 7e675b27..0f80fb62 100644 --- a/FreeSql/Sqlite/Curd/SqliteUpdate.cs +++ b/FreeSql/Sqlite/Curd/SqliteUpdate.cs @@ -15,10 +15,16 @@ namespace FreeSql.Sqlite.Curd { : base(orm, commonUtils, commonExpression, dywhere) { } - public override List ExecuteUpdated() { + public override int ExecuteAffrows() => base.SplitExecuteAffrows(200, 999); + public override Task ExecuteAffrowsAsync() => base.SplitExecuteAffrowsAsync(200, 999); + public override List ExecuteUpdated() => base.SplitExecuteUpdated(200, 999); + public override Task> ExecuteUpdatedAsync() => base.SplitExecuteUpdatedAsync(200, 999); + + + internal override List RawExecuteUpdated() { throw new NotImplementedException(); } - public override Task> ExecuteUpdatedAsync() { + internal override Task> RawExecuteUpdatedAsync() { throw new NotImplementedException(); }