From 4edfb04010e0945c2488bc6935eabce0a620efa6 Mon Sep 17 00:00:00 2001 From: 28810 <28810@YEXIANGQIN> Date: Fri, 29 Mar 2019 12:58:58 +0800 Subject: [PATCH] =?UTF-8?q?-=20=E5=A2=9E=E5=8A=A0=20IUpdate.WhereCaseSourc?= =?UTF-8?q?e=20=E6=96=B9=E6=B3=95=EF=BC=8C=E5=AE=9E=E7=8E=B0=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E4=BF=AE=E6=94=B9=E6=97=B6=E7=9A=84=E6=9D=A1=E4=BB=B6?= =?UTF-8?q?=E5=88=A4=E6=96=AD=EF=BC=9B=20-=20=E5=A2=9E=E5=8A=A0=20FreeSql.?= =?UTF-8?q?DbContext=20=E8=A1=8C=E7=BA=A7=E9=94=81=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/ValuesController.cs | 3 +- .../dbcontext_01/DbContexts/SongContext.cs | 3 + Examples/dbcontext_01/Startup.cs | 1 + .../DataAnnotations/VersionAttribute.cs | 8 +++ .../{ => DbContext}/DbContext.cs | 0 .../{ => DbContext}/DbContextAsync.cs | 0 .../DbContextOptionsBuilder.cs | 1 + .../{ => DbContext}/DbContextSync.cs | 0 FreeSql.DbContext/{ => DbSet}/DbSet.cs | 64 ++++++++++++------- FreeSql.DbContext/{ => DbSet}/DbSetAsync.cs | 0 FreeSql.DbContext/{ => DbSet}/DbSetSync.cs | 64 +++++++++++++++---- .../{ => Extenssions}/DependencyInjection.cs | 0 .../{ => Extenssions}/EntityUtil.cs | 58 +++++++++++++++++ FreeSql.DbContext/FreeSql.DbContext.csproj | 2 +- FreeSql.Repository/FreeSql.Repository.csproj | 2 +- FreeSql/FreeSql.csproj | 2 +- FreeSql/Interface/Curd/IUpdate.cs | 7 ++ .../Internal/CommonProvider/UpdateProvider.cs | 44 ++++++++++++- 18 files changed, 220 insertions(+), 39 deletions(-) create mode 100644 FreeSql.DbContext/DataAnnotations/VersionAttribute.cs rename FreeSql.DbContext/{ => DbContext}/DbContext.cs (100%) rename FreeSql.DbContext/{ => DbContext}/DbContextAsync.cs (100%) rename FreeSql.DbContext/{ => DbContext}/DbContextOptionsBuilder.cs (99%) rename FreeSql.DbContext/{ => DbContext}/DbContextSync.cs (100%) rename FreeSql.DbContext/{ => DbSet}/DbSet.cs (71%) rename FreeSql.DbContext/{ => DbSet}/DbSetAsync.cs (100%) rename FreeSql.DbContext/{ => DbSet}/DbSetSync.cs (82%) rename FreeSql.DbContext/{ => Extenssions}/DependencyInjection.cs (100%) rename FreeSql.DbContext/{ => Extenssions}/EntityUtil.cs (82%) diff --git a/Examples/dbcontext_01/Controllers/ValuesController.cs b/Examples/dbcontext_01/Controllers/ValuesController.cs index 58456a6f..8234832e 100644 --- a/Examples/dbcontext_01/Controllers/ValuesController.cs +++ b/Examples/dbcontext_01/Controllers/ValuesController.cs @@ -50,7 +50,7 @@ namespace dbcontext_01.Controllers ctx.Songs.AddRange(adds); //立即执行,将自增值赋给 adds 所有元素,因为有自增类型,如果其他类型,指定传入主键值,不会立即执行 - for (var a = 0; a < adds.Count; a++) + for (var a = 0; a < 10; a++) adds[a].Title = "dkdkdkdk" + a; ctx.Songs.UpdateRange(adds); @@ -63,6 +63,7 @@ namespace dbcontext_01.Controllers adds.Last().Url = "skldfjlksdjglkjjcccc"; ctx.Songs.Update(adds.Last()); + //单条修改 urls 的值,进入队列 //throw new Exception("回滚"); diff --git a/Examples/dbcontext_01/DbContexts/SongContext.cs b/Examples/dbcontext_01/DbContexts/SongContext.cs index 2a04d969..74b4ea71 100644 --- a/Examples/dbcontext_01/DbContexts/SongContext.cs +++ b/Examples/dbcontext_01/DbContexts/SongContext.cs @@ -25,6 +25,9 @@ namespace dbcontext_01 { public string Url { get; set; } public virtual ICollection Tags { get; set; } + + [Version] + public long versionRow { get; set; } } public class Song_tag { public int Song_id { get; set; } diff --git a/Examples/dbcontext_01/Startup.cs b/Examples/dbcontext_01/Startup.cs index eee2e446..4a0edf5a 100644 --- a/Examples/dbcontext_01/Startup.cs +++ b/Examples/dbcontext_01/Startup.cs @@ -25,6 +25,7 @@ namespace dbcontext_01 .UseLogger(loggerFactory.CreateLogger()) .UseAutoSyncStructure(true) .UseLazyLoading(true) + .UseNoneCommandParameter(true) .UseMonitorCommand(cmd => Trace.WriteLine(cmd.CommandText), (cmd, log) => Trace.WriteLine(log) diff --git a/FreeSql.DbContext/DataAnnotations/VersionAttribute.cs b/FreeSql.DbContext/DataAnnotations/VersionAttribute.cs new file mode 100644 index 00000000..b25e708f --- /dev/null +++ b/FreeSql.DbContext/DataAnnotations/VersionAttribute.cs @@ -0,0 +1,8 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace FreeSql { + public class VersionAttribute : Attribute { + } +} diff --git a/FreeSql.DbContext/DbContext.cs b/FreeSql.DbContext/DbContext/DbContext.cs similarity index 100% rename from FreeSql.DbContext/DbContext.cs rename to FreeSql.DbContext/DbContext/DbContext.cs diff --git a/FreeSql.DbContext/DbContextAsync.cs b/FreeSql.DbContext/DbContext/DbContextAsync.cs similarity index 100% rename from FreeSql.DbContext/DbContextAsync.cs rename to FreeSql.DbContext/DbContext/DbContextAsync.cs diff --git a/FreeSql.DbContext/DbContextOptionsBuilder.cs b/FreeSql.DbContext/DbContext/DbContextOptionsBuilder.cs similarity index 99% rename from FreeSql.DbContext/DbContextOptionsBuilder.cs rename to FreeSql.DbContext/DbContext/DbContextOptionsBuilder.cs index c8d75785..793336f4 100644 --- a/FreeSql.DbContext/DbContextOptionsBuilder.cs +++ b/FreeSql.DbContext/DbContext/DbContextOptionsBuilder.cs @@ -9,6 +9,7 @@ namespace FreeSql { public class DbContextOptionsBuilder { internal IFreeSql _fsql; + public DbContextOptionsBuilder UseFreeSql(IFreeSql orm) { _fsql = orm; return this; diff --git a/FreeSql.DbContext/DbContextSync.cs b/FreeSql.DbContext/DbContext/DbContextSync.cs similarity index 100% rename from FreeSql.DbContext/DbContextSync.cs rename to FreeSql.DbContext/DbContext/DbContextSync.cs diff --git a/FreeSql.DbContext/DbSet.cs b/FreeSql.DbContext/DbSet/DbSet.cs similarity index 71% rename from FreeSql.DbContext/DbSet.cs rename to FreeSql.DbContext/DbSet/DbSet.cs index fabad558..daf03c5a 100644 --- a/FreeSql.DbContext/DbSet.cs +++ b/FreeSql.DbContext/DbSet/DbSet.cs @@ -15,32 +15,50 @@ using FreeSql.Extensions; namespace FreeSql { public abstract partial class DbSet where TEntity : class { - protected DbContext _ctx; + internal DbContext _ctx; IFreeSql _fsql => _ctx._fsql; - protected ISelect OrmSelect(object dywhere) { + ISelect OrmSelect(object dywhere) { _ctx.ExecCommand(); //查询前先提交,否则会出脏读 return _fsql.Select(dywhere).WithTransaction(_ctx.GetOrBeginTransaction(false)).TrackToList(TrackToList); } - protected IInsert OrmInsert() => _fsql.Insert().WithTransaction(_ctx.GetOrBeginTransaction()); - protected IInsert OrmInsert(TEntity data) => _fsql.Insert(data).WithTransaction(_ctx.GetOrBeginTransaction()); - protected IInsert OrmInsert(TEntity[] data) => _fsql.Insert(data).WithTransaction(_ctx.GetOrBeginTransaction()); - protected IInsert OrmInsert(IEnumerable data) => _fsql.Insert(data).WithTransaction(_ctx.GetOrBeginTransaction()); + 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()); - protected IUpdate OrmUpdate(object dywhere) => _fsql.Update(dywhere).WithTransaction(_ctx.GetOrBeginTransaction()); - protected IDelete OrmDelete(object dywhere) => _fsql.Delete(dywhere).WithTransaction(_ctx.GetOrBeginTransaction()); + IUpdate OrmUpdate(object dywhere) => _fsql.Update(dywhere).WithTransaction(_ctx.GetOrBeginTransaction()); + IDelete OrmDelete(object dywhere) => _fsql.Delete(dywhere).WithTransaction(_ctx.GetOrBeginTransaction()); public ISelect Select => this.OrmSelect(null); public ISelect Where(Expression> exp) => this.OrmSelect(null).Where(exp); public ISelect WhereIf(bool condition, Expression> exp) => this.OrmSelect(null).WhereIf(condition, exp); - protected Dictionary _states = new Dictionary(); + Dictionary _states = new Dictionary(); TableInfo _tablePriv; - protected TableInfo _table => _tablePriv ?? (_tablePriv = _fsql.CodeFirst.GetTableByEntity(_entityType)); + TableInfo _table => _tablePriv ?? (_tablePriv = _fsql.CodeFirst.GetTableByEntity(_entityType)); ColumnInfo[] _tableIdentitysPriv; - protected ColumnInfo[] _tableIdentitys => _tableIdentitysPriv ?? (_tableIdentitysPriv = _table.Primarys.Where(a => a.Attribute.IsIdentity).ToArray()); - protected Type _entityType = typeof(TEntity); + 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) { @@ -54,20 +72,20 @@ namespace FreeSql { } #region Utils - protected EntityState CreateEntityState(TEntity data) { + EntityState CreateEntityState(TEntity data) { if (data == null) throw new ArgumentNullException(nameof(data)); var key = _fsql.GetEntityKeyString(data); var state = new EntityState(Activator.CreateInstance(), key); _fsql.MapEntityValue(data, state.Value); return state; } - protected bool ExistsInStates(TEntity data) { + bool ExistsInStates(TEntity data) { if (data == null) throw new ArgumentNullException(nameof(data)); var key = _fsql.GetEntityKeyString(data); if (string.IsNullOrEmpty(key)) return false; return _states.ContainsKey(key); } - protected bool CanAdd(TEntity[] data, bool isThrow) { + bool CanAdd(TEntity[] data, bool isThrow) { if (data == null) { if (isThrow) throw new ArgumentNullException(nameof(data)); return false; @@ -75,7 +93,7 @@ namespace FreeSql { foreach (var s in data) if (CanAdd(s, isThrow) == false) return false; return true; } - protected bool CanAdd(IEnumerable data, bool isThrow) { + bool CanAdd(IEnumerable data, bool isThrow) { if (data == null) { if (isThrow) throw new ArgumentNullException(nameof(data)); return false; @@ -83,7 +101,7 @@ namespace FreeSql { foreach (var s in data) if (CanAdd(s, isThrow) == false) return false; return true; } - protected bool CanAdd(TEntity data, bool isThrow) { + bool CanAdd(TEntity data, bool isThrow) { if (data == null) { if (isThrow) throw new ArgumentNullException(nameof(data)); return false; @@ -117,7 +135,7 @@ namespace FreeSql { return true; } - protected bool CanUpdate(TEntity[] data, bool isThrow) { + bool CanUpdate(TEntity[] data, bool isThrow) { if (data == null) { if (isThrow) throw new ArgumentNullException(nameof(data)); return false; @@ -125,7 +143,7 @@ namespace FreeSql { foreach (var s in data) if (CanUpdate(s, isThrow) == false) return false; return true; } - protected bool CanUpdate(IEnumerable data, bool isThrow) { + bool CanUpdate(IEnumerable data, bool isThrow) { if (data == null) { if (isThrow) throw new ArgumentNullException(nameof(data)); return false; @@ -133,7 +151,7 @@ namespace FreeSql { foreach (var s in data) if (CanUpdate(s, isThrow) == false) return false; return true; } - protected bool CanUpdate(TEntity data, bool isThrow) { + bool CanUpdate(TEntity data, bool isThrow) { if (data == null) { if (isThrow) throw new ArgumentNullException(nameof(data)); return false; @@ -154,7 +172,7 @@ namespace FreeSql { return true; } - protected bool CanRemove(TEntity[] data, bool isThrow) { + bool CanRemove(TEntity[] data, bool isThrow) { if (data == null) { if (isThrow) throw new ArgumentNullException(nameof(data)); return false; @@ -162,7 +180,7 @@ namespace FreeSql { foreach (var s in data) if (CanRemove(s, isThrow) == false) return false; return true; } - protected bool CanRemove(IEnumerable data, bool isThrow) { + bool CanRemove(IEnumerable data, bool isThrow) { if (data == null) { if (isThrow) throw new ArgumentNullException(nameof(data)); return false; @@ -170,7 +188,7 @@ namespace FreeSql { foreach (var s in data) if (CanRemove(s, isThrow) == false) return false; return true; } - protected bool CanRemove(TEntity data, bool isThrow) { + bool CanRemove(TEntity data, bool isThrow) { if (data == null) { if (isThrow) throw new ArgumentNullException(nameof(data)); return false; diff --git a/FreeSql.DbContext/DbSetAsync.cs b/FreeSql.DbContext/DbSet/DbSetAsync.cs similarity index 100% rename from FreeSql.DbContext/DbSetAsync.cs rename to FreeSql.DbContext/DbSet/DbSetAsync.cs diff --git a/FreeSql.DbContext/DbSetSync.cs b/FreeSql.DbContext/DbSet/DbSetSync.cs similarity index 82% rename from FreeSql.DbContext/DbSetSync.cs rename to FreeSql.DbContext/DbSet/DbSetSync.cs index acc92a75..601949d3 100644 --- a/FreeSql.DbContext/DbSetSync.cs +++ b/FreeSql.DbContext/DbSet/DbSetSync.cs @@ -148,26 +148,68 @@ namespace FreeSql { var cuig1 = _fsql.CompareEntityValueReturnColumns(uplst1.Value, lstval1.Value, true); var cuig2 = uplst2 != null ? _fsql.CompareEntityValueReturnColumns(uplst2.Value, lstval2.Value, true) : null; + + List data = null; + string[] cuig = null; if (uplst2 != null && string.Compare(string.Join(",", cuig1), string.Join(",", cuig2)) != 0) { //最后一个不保存 - var data = ups.ToList(); + data = ups.ToList(); data.RemoveAt(ups.Length - 1); - var affrows = this.OrmUpdate(null).SetSource(data.Select(a => a.Value)).IgnoreColumns(cuig2).ExecuteAffrows(); + cuig = cuig2; + } else if (isLiveUpdate) { + //立即保存 + data = ups.ToList(); + cuig = cuig1; + } + + if (data?.Count > 0) { + + if (cuig.Length == _table.Columns.Count) + 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 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); } return affrows; - } else if (isLiveUpdate) { - //立即保存 - var data = ups; - var affrows = this.OrmUpdate(null).SetSource(data.Select(a => a.Value)).IgnoreColumns(cuig1).ExecuteAffrows(); - foreach (var newval in data) { - if (_states.TryGetValue(newval.Key, out var tryold)) - _fsql.MapEntityValue(newval.Value, tryold.Value); - } - return Math.Min(ups.Length, affrows); } + //等待下次对比再保存 return 0; } diff --git a/FreeSql.DbContext/DependencyInjection.cs b/FreeSql.DbContext/Extenssions/DependencyInjection.cs similarity index 100% rename from FreeSql.DbContext/DependencyInjection.cs rename to FreeSql.DbContext/Extenssions/DependencyInjection.cs diff --git a/FreeSql.DbContext/EntityUtil.cs b/FreeSql.DbContext/Extenssions/EntityUtil.cs similarity index 82% rename from FreeSql.DbContext/EntityUtil.cs rename to FreeSql.DbContext/Extenssions/EntityUtil.cs index 54b97249..0eebeb83 100644 --- a/FreeSql.DbContext/EntityUtil.cs +++ b/FreeSql.DbContext/Extenssions/EntityUtil.cs @@ -303,5 +303,63 @@ namespace FreeSql.Extensions { }); return func(up, oldval, isEqual); } + + static ConcurrentDictionary>> _dicSetEntityIncrByWithPropertyName = new ConcurrentDictionary>>(); + /// + /// 设置实体中某属性的数值增加指定的值 + /// + /// + /// + /// + /// + public static void SetEntityIncrByWithPropertyName(this IFreeSql orm, TEntity item, string propertyName, int incrBy) { + var func = _dicSetEntityIncrByWithPropertyName.GetOrAdd(orm.Ado.DataType, dt => new ConcurrentDictionary>()).GetOrAdd(typeof(TEntity), t => { + var _table = orm.CodeFirst.GetTableByEntity(t); + var parm1 = Expression.Parameter(typeof(object)); + var parm2 = Expression.Parameter(typeof(string)); + var parm3 = Expression.Parameter(typeof(int)); + var var1Parm = Expression.Variable(t); + var exps = new List(new Expression[] { + Expression.Assign(var1Parm, Expression.TypeAs(parm1, t)) + }); + if (_table.Properties.ContainsKey(propertyName)) { + var prop = _table.Properties[propertyName]; + exps.Add( + Expression.Assign( + Expression.MakeMemberAccess(var1Parm, prop), + Expression.Add( + Expression.MakeMemberAccess(var1Parm, prop), + Expression.Convert( + FreeSql.Internal.Utils.GetDataReaderValueBlockExpression(prop.PropertyType, Expression.Convert(parm3, typeof(object))), + prop.PropertyType + ) + ) + ) + ); + } + return Expression.Lambda>(Expression.Block(new[] { var1Parm }, exps), new[] { parm1, parm2, parm3 }).Compile(); + }); + func(item, propertyName, incrBy); + } + + static ConcurrentDictionary _dicAppendEntityUpdateSetWithColumnMethods = new ConcurrentDictionary(); + static ConcurrentDictionary> _dicAppendEntityUpdateSetWithColumnMethod = new ConcurrentDictionary>(); + /// + /// 缓存执行 IUpdate.Set + /// + /// + /// + /// + /// + public static void AppendEntityUpdateSetWithColumn(this IUpdate update, Type columnType, LambdaExpression setExp) where TEntity : class { + + var setMethod = _dicAppendEntityUpdateSetWithColumnMethod.GetOrAdd(typeof(IUpdate), uptp => new ConcurrentDictionary()).GetOrAdd(columnType, coltp => { + var allMethods = _dicAppendEntityUpdateSetWithColumnMethods.GetOrAdd(typeof(IUpdate), uptp => uptp.GetMethods()); + return allMethods.Where(a => a.Name == "Set" && a.IsGenericMethod && a.GetParameters().Length == 1 && a.GetGenericArguments().First().Name == "TMember").FirstOrDefault() + .MakeGenericMethod(columnType); + }); + + setMethod.Invoke(update, new object[] { setExp }); + } } } diff --git a/FreeSql.DbContext/FreeSql.DbContext.csproj b/FreeSql.DbContext/FreeSql.DbContext.csproj index 398a5d1f..6bb82389 100644 --- a/FreeSql.DbContext/FreeSql.DbContext.csproj +++ b/FreeSql.DbContext/FreeSql.DbContext.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.3.216 + 0.3.27 true YeXiangQin FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite. diff --git a/FreeSql.Repository/FreeSql.Repository.csproj b/FreeSql.Repository/FreeSql.Repository.csproj index 6d7b2b1a..38e5b67f 100644 --- a/FreeSql.Repository/FreeSql.Repository.csproj +++ b/FreeSql.Repository/FreeSql.Repository.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.3.26 + 0.3.27 YeXiangQin FreeSql Implementation of General Repository, Support MySql/SqlServer/PostgreSQL/Oracle/Sqlite, and read/write separation、and split table. https://github.com/2881099/FreeSql/wiki/Repository diff --git a/FreeSql/FreeSql.csproj b/FreeSql/FreeSql.csproj index e175b2e0..fcbd8787 100644 --- a/FreeSql/FreeSql.csproj +++ b/FreeSql/FreeSql.csproj @@ -2,7 +2,7 @@ netstandard2.0 - 0.3.26 + 0.3.27 true YeXiangQin FreeSql is the most convenient ORM in dotnet. It supports Mysql, Postgresql, SqlServer, Oracle and Sqlite. diff --git a/FreeSql/Interface/Curd/IUpdate.cs b/FreeSql/Interface/Curd/IUpdate.cs index 6c477c51..9daca4ab 100644 --- a/FreeSql/Interface/Curd/IUpdate.cs +++ b/FreeSql/Interface/Curd/IUpdate.cs @@ -101,6 +101,13 @@ 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/UpdateProvider.cs b/FreeSql/Internal/CommonProvider/UpdateProvider.cs index 79402cc5..a3758662 100644 --- a/FreeSql/Internal/CommonProvider/UpdateProvider.cs +++ b/FreeSql/Internal/CommonProvider/UpdateProvider.cs @@ -20,6 +20,7 @@ namespace FreeSql.Internal.CommonProvider { protected Func _tableRule; protected StringBuilder _where = new StringBuilder(); protected StringBuilder _set = new StringBuilder(); + protected StringBuilder _setIncr = new StringBuilder(); protected List _params = new List(); protected List _paramsSource = new List(); protected bool _noneParameter; @@ -40,6 +41,7 @@ namespace FreeSql.Internal.CommonProvider { _ignore.Clear(); _where.Clear(); _set.Clear(); + _setIncr.Clear(); _params.Clear(); _paramsSource.Clear(); } @@ -117,7 +119,7 @@ namespace FreeSql.Internal.CommonProvider { expt = expt.Replace(replname, _commonUtils.IsNull(replname, _commonUtils.FormatSql("{0}", replval))); } } - _set.Append(", ").Append(_commonUtils.QuoteSqlName(cols.First().Column.Attribute.Name)).Append(" = ").Append(expt); + _setIncr.Append(", ").Append(_commonUtils.QuoteSqlName(cols.First().Column.Attribute.Name)).Append(" = ").Append(expt); return this; } public IUpdate SetRaw(string sql, object parms = null) { @@ -142,6 +144,45 @@ 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; + 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 == 1) { + + var col = _table.ColumnsByCs[CsName]; + var sb = new StringBuilder(); + + sb.Append(_commonUtils.QuoteSqlName(col.Attribute.Name)).Append(" = "); + 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()); + + } else { + var caseWhen = new StringBuilder(); + caseWhen.Append("CASE "); + ToSqlCase(caseWhen, _table.Primarys); + var cw = caseWhen.ToString(); + + 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"); + + return this.Where(sb.ToString()); + } + } + protected abstract void ToSqlCase(StringBuilder caseWhen, ColumnInfo[] primarys); protected abstract void ToSqlWhen(StringBuilder sb, ColumnInfo[] primarys, object d); @@ -227,6 +268,7 @@ namespace FreeSql.Internal.CommonProvider { } else return null; + sb.Append(_setIncr.ToString()); sb.Append(" \r\nWHERE ").Append(_where.ToString().Substring(5)); return sb.ToString(); }