diff --git a/Src/Asp.NetCore2/SqlSugar/Abstract/SugarProvider/SqlSugarProvider.cs b/Src/Asp.NetCore2/SqlSugar/Abstract/SugarProvider/SqlSugarProvider.cs index 26ccf672d..c0dd2737e 100644 --- a/Src/Asp.NetCore2/SqlSugar/Abstract/SugarProvider/SqlSugarProvider.cs +++ b/Src/Asp.NetCore2/SqlSugar/Abstract/SugarProvider/SqlSugarProvider.cs @@ -1342,6 +1342,27 @@ namespace SqlSugar #endregion #region Other + public void Tracking(T data) where T : class, new() + { + if (data != null) + { + UtilMethods.IsNullReturnNew(this.TempItems); + var key = "Tracking_" + data.GetHashCode() + ""; + if (!this.TempItems.ContainsKey(key)) + { + var newT = new T(); + FastCopy.Copy(data, newT); + this.TempItems.Add(key, newT); + } + } + } + public void Tracking(List datas) where T : class, new() + { + foreach (var data in datas) + { + this.Tracking(data); + } + } public SqlSugarClient CopyNew() { return new SqlSugarClient(UtilMethods.CopyConfig(this.Ado.Context.CurrentConnectionConfig)); diff --git a/Src/Asp.NetCore2/SqlSugar/Abstract/SugarProvider/SqlSugarScopeProvider.cs b/Src/Asp.NetCore2/SqlSugar/Abstract/SugarProvider/SqlSugarScopeProvider.cs index 366e384b8..eee6023e4 100644 --- a/Src/Asp.NetCore2/SqlSugar/Abstract/SugarProvider/SqlSugarScopeProvider.cs +++ b/Src/Asp.NetCore2/SqlSugar/Abstract/SugarProvider/SqlSugarScopeProvider.cs @@ -710,6 +710,14 @@ namespace SqlSugar { return new SqlSugarClient(UtilMethods.CopyConfig(this.Ado.Context.CurrentConnectionConfig)); } + public void Tracking(T data) where T : class, new() + { + ScopedContext.Tracking(data); + } + public void Tracking(List datas) where T : class, new() + { + ScopedContext.Tracking(datas); + } #endregion } } \ No newline at end of file diff --git a/Src/Asp.NetCore2/SqlSugar/Abstract/UpdateProvider/UpdateableHelper.cs b/Src/Asp.NetCore2/SqlSugar/Abstract/UpdateProvider/UpdateableHelper.cs index 86edea5ba..ced0e842e 100644 --- a/Src/Asp.NetCore2/SqlSugar/Abstract/UpdateProvider/UpdateableHelper.cs +++ b/Src/Asp.NetCore2/SqlSugar/Abstract/UpdateProvider/UpdateableHelper.cs @@ -85,6 +85,7 @@ namespace SqlSugar { DataAop(item); SetUpdateItemByEntity(i, item, updateItem); + Tracking(item); } ++i; } @@ -97,6 +98,33 @@ namespace SqlSugar } } + private void Tracking(T item) + { + if (IsTrakingData()) + { + var trackingData = this.Context.TempItems.FirstOrDefault(it => it.Key.StartsWith("Tracking_" + item.GetHashCode())); + var diffColumns = FastCopy.GetDiff(item, (T)trackingData.Value); + if (diffColumns.Count > 0) + { + var pks =EntityInfo.Columns + .Where(it => it.IsPrimarykey).Select(it => it.PropertyName).ToList(); + diffColumns=diffColumns.Where(it => !pks.Contains(it)).ToList(); + if (diffColumns.Count > 0) + { + this.UpdateColumns(diffColumns.ToArray()); + } + } + } + } + + private bool IsTrakingData() + { + return this.UpdateParameterIsNull == false + && this.Context.TempItems != null + && this.Context.TempItems.Any(it => it.Key.StartsWith("Tracking_")) + && this.UpdateObjs.Length == 1; + } + private void DataAop(T item) { var dataEvent = this.Context.CurrentConnectionConfig.AopEvents?.DataExecuting; diff --git a/Src/Asp.NetCore2/SqlSugar/Interface/ISqlSugarClient.cs b/Src/Asp.NetCore2/SqlSugar/Interface/ISqlSugarClient.cs index 2d4e124ab..26ad3abbe 100644 --- a/Src/Asp.NetCore2/SqlSugar/Interface/ISqlSugarClient.cs +++ b/Src/Asp.NetCore2/SqlSugar/Interface/ISqlSugarClient.cs @@ -44,6 +44,8 @@ namespace SqlSugar #endregion #region Other methods + void Tracking(T data) where T : class, new(); + void Tracking(List data) where T : class, new(); SqlSugarClient CopyNew(); T CreateContext(bool isTran=true) where T : SugarUnitOfWork, new(); SugarUnitOfWork CreateContext(bool isTran = true); diff --git a/Src/Asp.NetCore2/SqlSugar/Realization/SqlServer/DbMaintenance/SqlServerDbMaintenance.cs b/Src/Asp.NetCore2/SqlSugar/Realization/SqlServer/DbMaintenance/SqlServerDbMaintenance.cs index d19670c89..921aa931d 100644 --- a/Src/Asp.NetCore2/SqlSugar/Realization/SqlServer/DbMaintenance/SqlServerDbMaintenance.cs +++ b/Src/Asp.NetCore2/SqlSugar/Realization/SqlServer/DbMaintenance/SqlServerDbMaintenance.cs @@ -179,7 +179,7 @@ namespace SqlSugar { get { - return "EXECUTE sp_addextendedproperty N'MS_Description', '{2}', N'user', N'dbo', N'table', N'{1}', N'column', N'{0}'"; ; + return "EXECUTE sp_addextendedproperty N'MS_Description', N'{2}', N'user', N'dbo', N'table', N'{1}', N'column', N'{0}'"; ; } } @@ -440,7 +440,6 @@ namespace SqlSugar } var oldDatabaseName = this.Context.Ado.Connection.Database; var connection = this.Context.CurrentConnectionConfig.ConnectionString; - Check.ExceptionEasy(String.IsNullOrEmpty(connection), "ConnectionString is not null", "连接字符串ConnectionString不能为Null"); connection = connection.Replace(oldDatabaseName, "master"); var newDb = new SqlSugarClient(new ConnectionConfig() { diff --git a/Src/Asp.NetCore2/SqlSugar/SqlSugarClient.cs b/Src/Asp.NetCore2/SqlSugar/SqlSugarClient.cs index b4cefcba3..cb0452236 100644 --- a/Src/Asp.NetCore2/SqlSugar/SqlSugarClient.cs +++ b/Src/Asp.NetCore2/SqlSugar/SqlSugarClient.cs @@ -996,6 +996,14 @@ namespace SqlSugar #endregion #region Other method + public void Tracking(T data) where T : class, new() + { + this.Context.Tracking(data); + } + public void Tracking(List datas) where T : class, new() + { + this.Context.Tracking(datas); + } public SqlSugarClient CopyNew() { return new SqlSugarClient(UtilMethods.CopyConfig(this.Ado.Context.CurrentConnectionConfig)); diff --git a/Src/Asp.NetCore2/SqlSugar/SqlSugarScope.cs b/Src/Asp.NetCore2/SqlSugar/SqlSugarScope.cs index c56846a53..5d2b9b4d2 100644 --- a/Src/Asp.NetCore2/SqlSugar/SqlSugarScope.cs +++ b/Src/Asp.NetCore2/SqlSugar/SqlSugarScope.cs @@ -766,5 +766,13 @@ namespace SqlSugar { return new SqlSugarClient(UtilMethods.CopyConfig(this.Ado.Context.CurrentConnectionConfig)); } + public void Tracking(T data) where T : class, new() + { + ScopedContext.Tracking(data); + } + public void Tracking(List datas) where T : class, new() + { + ScopedContext.Tracking(datas); + } } } diff --git a/Src/Asp.NetCore2/SqlSugar/Utilities/FastCopy.cs b/Src/Asp.NetCore2/SqlSugar/Utilities/FastCopy.cs new file mode 100644 index 000000000..dc38f5ad0 --- /dev/null +++ b/Src/Asp.NetCore2/SqlSugar/Utilities/FastCopy.cs @@ -0,0 +1,130 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using static System.Linq.Expressions.Expression; + +namespace SqlSugar +{ + internal static class FastCopy + { + static ConcurrentDictionary copiers = new ConcurrentDictionary(); + + /// + /// 复制两个对象同名属性值 + /// + /// + /// + /// 源对象 + /// 目标对象 + /// 源对象属性值为null时,是否将值复制给目标对象 + public static void Copy(S source, T target, bool copyNull = true) + { + string name = string.Format("{0}_{1}_{2}", typeof(S), typeof(T), copyNull); + + object targetCopier; + if (!copiers.TryGetValue(name, out targetCopier)) + { + Action copier = CreateCopier(copyNull); + copiers.TryAdd(name, copier); + targetCopier = copier; + } + + Action action = (Action)targetCopier; + action(source, target); + } + + /// + /// 为指定的两种类型编译生成属性复制委托 + /// + /// + /// + /// 源对象属性值为null时,是否将值复制给目标对象 + /// + private static Action CreateCopier(bool copyNull) + { + ParameterExpression source = Parameter(typeof(S)); + ParameterExpression target = Parameter(typeof(T)); + var sourceProps = typeof(S).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanRead).ToList(); + var targetProps = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public).Where(p => p.CanWrite).ToList(); + + // 查找可进行赋值的属性 + var copyProps = targetProps.Where(tProp => sourceProps.Where(sProp => sProp.Name == tProp.Name// 名称一致 且 + && ( + sProp.PropertyType == tProp.PropertyType// 属性类型一致 或 + || sProp.PropertyType.IsAssignableFrom(tProp.PropertyType) // 源属性类型 为 目标属性类型 的 子类;eg:object target = string source; 或 + || (tProp.PropertyType.IsValueType && sProp.PropertyType.IsValueType && // 属性为值类型且基础类型一致,但目标属性为可空类型 eg:int? num = int num; + ((tProp.PropertyType.GenericTypeArguments.Length > 0 ? tProp.PropertyType.GenericTypeArguments[0] : tProp.PropertyType) == sProp.PropertyType)) + )).Count() > 0); + + List expressionList = new List(); + foreach (var prop in copyProps) + { + if (prop.PropertyType.IsValueType)// 属性为值类型 + { + PropertyInfo sProp = typeof(S).GetProperty(prop.Name); + PropertyInfo tProp = typeof(T).GetProperty(prop.Name); + if (sProp.PropertyType == tProp.PropertyType)// 属性类型一致 eg:int num = int num; 或 int? num = int? num; + { + var assign = Assign(Property(target, prop.Name), Property(source, prop.Name)); + expressionList.Add(assign); + } + else if (sProp.PropertyType.GenericTypeArguments.Length <= 0 && tProp.PropertyType.GenericTypeArguments.Length > 0)// 属性类型不一致且目标属性类型为可空类型 eg:int? num = int num; + { + var convert = Convert(Expression.Property(source, prop.Name), tProp.PropertyType); + var cvAssign = Assign(Expression.Property(target, prop.Name), convert); + expressionList.Add(cvAssign); + } + } + else// 属性为引用类型 + { + var assign = Assign(Property(target, prop.Name), Property(source, prop.Name));// 编译生成属性赋值语句 target.{PropertyName} = source.{PropertyName}; + var sourcePropIsNull = Equal(Constant(null, prop.PropertyType), Property(source, prop.Name));// 判断源属性值是否为Null;编译生成 source.{PropertyName} == null + var setNull = IsTrue(Constant(copyNull));// 判断是否复制Null值 编译生成 copyNull == True + var setNullTest = IfThen(setNull, assign); + var condition = IfThenElse(sourcePropIsNull, setNullTest, assign); + + /** + * 编译生成 + * if(source.{PropertyName} == null) + * { + * if(setNull) + * { + * target.{PropertyName} = source.{PropertyName}; + * } + * } + * else + * { + * target.{PropertyName} = source.{PropertyName}; + * } + */ + expressionList.Add(condition); + } + } + var block = Block(expressionList.ToArray()); + Expression> lambda = Lambda>(block, source, target); + return lambda.Compile(); + } + + internal static List GetDiff(T item, T trackingData) where T : class, new() + { + List result = new List(); + foreach (var t in typeof(T).GetProperties()) + { + var leftvalue = t.GetValue(item); + var rightvalue = t.GetValue(trackingData); + if ((leftvalue == null && rightvalue != null)) + { + result.Add(t.Name); + } + else if (leftvalue!=null&&!leftvalue.Equals(rightvalue)) + { + result.Add(t.Name); + } + } + return result; + } + } +} \ No newline at end of file