diff --git a/Src/Asp.NetCore2/SqlSugar/Abstract/QueryableProvider/QueryableHelper.cs b/Src/Asp.NetCore2/SqlSugar/Abstract/QueryableProvider/QueryableHelper.cs index d29c80e61..7df9d517e 100644 --- a/Src/Asp.NetCore2/SqlSugar/Abstract/QueryableProvider/QueryableHelper.cs +++ b/Src/Asp.NetCore2/SqlSugar/Abstract/QueryableProvider/QueryableHelper.cs @@ -1693,7 +1693,13 @@ namespace SqlSugar { var ps = this.QueryBuilder.Parameters; var itemProperty = typeof(TResult).GetProperty(subPara.Key); - var callType = itemProperty.PropertyType.GetGenericArguments()[0]; + var callType = itemProperty.PropertyType.GetGenericArguments().FirstOrDefault(); + var isFirst = false; + if (callType == null) + { + callType = itemProperty.PropertyType; + isFirst = true; + } var sql = subPara.Value.ObjToString().Replace("@sugarIndex", "0"); sql =SqlBuilder.RemoveParentheses(sql); var methodParamters = new object[] { sql, ps }; @@ -1704,14 +1710,31 @@ namespace SqlSugar var setValue = Activator.CreateInstance(itemProperty.PropertyType, true) as IList; if (typeof(TResult).IsAnonymousType()) { - var jobj = JObject.FromObject(item); - var prop = jobj.Property(itemProperty.Name); - prop.Value = JArray.FromObject(subList); - result[i] = jobj.ToObject(); + if (isFirst) + { + var jobj = JObject.FromObject(item); + var prop = jobj.Property(itemProperty.Name); + prop.Value = JObject.FromObject((subList as IList).Cast().FirstOrDefault()); + result[i] = jobj.ToObject(); + } + else + { + var jobj = JObject.FromObject(item); + var prop = jobj.Property(itemProperty.Name); + prop.Value = JArray.FromObject(subList); + result[i] = jobj.ToObject(); + } } else { - itemProperty.SetValue(item, subList); + if (isFirst) + { + itemProperty.SetValue(item, (subList as IList)[0] ); + } + else + { + itemProperty.SetValue(item, subList); + } } } } @@ -1741,10 +1764,14 @@ namespace SqlSugar index++; } - var itemProperty = typeof(TResult).GetProperty(subPara.Key); - var callType = itemProperty.PropertyType.GetGenericArguments()[0]; - + var callType = itemProperty.PropertyType.GetGenericArguments().FirstOrDefault(); + var isFirst = false; + if (callType == null) + { + callType = itemProperty.PropertyType; + isFirst = true; + } var sqlstring = string.Join(" \r\n UNION ALL ", sqls); var methodParamters = new object[] { sqlstring, ps.ToArray() }; this.QueryBuilder.SubToListParameters = null; @@ -1755,7 +1782,15 @@ namespace SqlSugar var subList = ExpressionBuilderHelper.CallFunc(callType, methodParamters, this.Clone(), "SubQueryList"); var appendValue = this.QueryBuilder.AppendValues; var list = (subList as IEnumerable).Cast().ToList(); - if (typeof(TResult).IsAnonymousType()) + if (isFirst && !typeof(TResult).IsAnonymousType()) + { + SetSubListWithClassFirst(result, itemProperty, appendValue, list, 0); + } + else if (isFirst && typeof(TResult).IsAnonymousType()) + { + SetSubListWithAnonymousTypeFirst(result, itemProperty, appendValue, list, 0); + } + else if (typeof(TResult).IsAnonymousType()) { SetSubListWithAnonymousType(result, itemProperty, appendValue, list, 0); } @@ -1791,6 +1826,60 @@ namespace SqlSugar } return resIndex; } + private static int SetSubListWithAnonymousTypeFirst(List result, PropertyInfo itemProperty, List> appendValue, List list, int resIndex) + { + for (int i = 0; i < result.Count; i++) + { + var item = result[i]; + object setValue = null; + if (appendValue != null) + { + var appindex = 0; + foreach (var appValue in appendValue) + { + if (appValue[0].Value.ObjToInt() == i) + { + setValue = list[appindex]; + //setValue.Add(addItem); + } + appindex++; + } + } + var jobj = JObject.FromObject(item); + var prop = jobj.Property(itemProperty.Name); + if (setValue != null) + { + prop.Value = JObject.FromObject(setValue); + } + result[i] = jobj.ToObject(); + //itemProperty.SetValue(item, setValue); + } + return resIndex; + } + private static int SetSubListWithClassFirst(List result, PropertyInfo itemProperty, List> appendValue, List list, int resIndex) + { + foreach (var item in result) + { + //var setValue = Activator.CreateInstance(itemProperty.PropertyType, true); + if (appendValue != null) + { + var appindex = 0; + foreach (var appValue in appendValue) + { + if (appValue[0].Value.ObjToInt() == resIndex) + { + var addItem = list[appindex]; + itemProperty.SetValue(item, addItem); + } + appindex++; + } + } + //itemProperty.SetValue(item, setValue); + resIndex++; + } + + return resIndex; + } private static int SetSubListWithClass(List result, PropertyInfo itemProperty, List> appendValue, List list, int resIndex) { diff --git a/Src/Asp.NetCore2/SqlSugar/Abstract/UpdateProvider/UpdateableHelper.cs b/Src/Asp.NetCore2/SqlSugar/Abstract/UpdateProvider/UpdateableHelper.cs index c090d0ce9..9abd8147f 100644 --- a/Src/Asp.NetCore2/SqlSugar/Abstract/UpdateProvider/UpdateableHelper.cs +++ b/Src/Asp.NetCore2/SqlSugar/Abstract/UpdateProvider/UpdateableHelper.cs @@ -11,14 +11,118 @@ namespace SqlSugar { public partial class UpdateableProvider : IUpdateable where T : class, new() { - + private bool IsUpdateNullByList() + { + return this.UpdateObjs.Count() > 1 && (this.UpdateBuilder.IsNoUpdateNull || this.UpdateBuilder.IsNoUpdateDefaultValue); + } + + private int DatasTrackingExecommand() + { + var trakRows = 0; + try + { + if (this.Context.Ado.IsNoTran()) + { + this.Context.Ado.BeginTran(); + } + int i = 0; + foreach (var item in this.UpdateObjs) + { + var newUpdateable = this.Clone(); + (newUpdateable as UpdateableProvider).UpdateObjs = new[] { item }; + newUpdateable.UpdateBuilder.IsListUpdate = null; + newUpdateable.UpdateBuilder.DbColumnInfoList = + newUpdateable.UpdateBuilder.DbColumnInfoList.Where(it => it.TableId == i).ToList(); + AppendTracking(item, newUpdateable); + if (newUpdateable.UpdateBuilder.DbColumnInfoList?.Any() == true) + { + trakRows += newUpdateable.ExecuteCommand(); + } + ++i; + } + if (this.Context.Ado.IsNoTran()) + { + this.Context.Ado.CommitTran(); + } + } + catch (Exception) + { + if (this.Context.Ado.IsNoTran()) + { + this.Context.Ado.RollbackTran(); + } + throw; + } + return trakRows; + } + private async Task DatasTrackingExecommandAsync() + { + var trakRows = 0; + try + { + if (this.Context.Ado.IsNoTran()) + { + await this.Context.Ado.BeginTranAsync(); + } + int i = 0; + foreach (var item in this.UpdateObjs) + { + var newUpdateable = this.Clone(); + (newUpdateable as UpdateableProvider).UpdateObjs = new[] { item }; + newUpdateable.UpdateBuilder.IsListUpdate = null; + newUpdateable.UpdateBuilder.DbColumnInfoList = + newUpdateable.UpdateBuilder.DbColumnInfoList.Where(it => it.TableId == i).ToList(); + AppendTracking(item, newUpdateable); + if (newUpdateable.UpdateBuilder.DbColumnInfoList?.Any() == true) + { + trakRows +=await newUpdateable.ExecuteCommandAsync(); + } + ++i; + } + if (this.Context.Ado.IsNoTran()) + { + await this.Context.Ado.CommitTranAsync(); + } + } + catch (Exception) + { + if (this.Context.Ado.IsNoTran()) + { + await this.Context.Ado.RollbackTranAsync(); + } + throw; + } + return trakRows; + } private bool UpdateObjectNotWhere() { return this.Context.CurrentConnectionConfig.DbType != DbType.MySql && this.Context.CurrentConnectionConfig.DbType != DbType.MySqlConnector && this.Context.CurrentConnectionConfig.DbType != DbType.SqlServer; } - + private void AppendTracking(T item, IUpdateable newUpdateable) + { + if (IsTrakingData() || IsTrakingDatas()) + { + 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) + { + newUpdateable.UpdateColumns(diffColumns.ToArray()); + } + } + else + { + (newUpdateable as UpdateableProvider).UpdateObjs = new T[] { null }; + newUpdateable.UpdateBuilder.DbColumnInfoList = new List(); + } + } + } private void AppendSets() { if (SetColumnsIndex > 0) @@ -139,6 +243,13 @@ namespace SqlSugar && this.UpdateObjs.Length == 1; } + private bool IsTrakingDatas() + { + 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/Abstract/UpdateProvider/UpdateableProvider.cs b/Src/Asp.NetCore2/SqlSugar/Abstract/UpdateProvider/UpdateableProvider.cs index 1bb5af2cb..d7254881b 100644 --- a/Src/Asp.NetCore2/SqlSugar/Abstract/UpdateProvider/UpdateableProvider.cs +++ b/Src/Asp.NetCore2/SqlSugar/Abstract/UpdateProvider/UpdateableProvider.cs @@ -80,6 +80,11 @@ namespace SqlSugar public virtual int ExecuteCommand() { + if (this.IsTrakingDatas() || IsUpdateNullByList()) + { + int trakRows = DatasTrackingExecommand(); + return trakRows; + } string sql = _ExecuteCommand(); if (string.IsNullOrEmpty(sql)) { @@ -90,6 +95,7 @@ namespace SqlSugar return result; } + public bool ExecuteCommandHasChange() { return this.ExecuteCommand() > 0; @@ -111,6 +117,11 @@ namespace SqlSugar public virtual async Task ExecuteCommandAsync() { + if (this.IsTrakingDatas()||IsUpdateNullByList()) + { + int trakRows =await DatasTrackingExecommandAsync(); + return trakRows; + } string sql = _ExecuteCommand(); if (string.IsNullOrEmpty(sql)) { @@ -128,6 +139,46 @@ namespace SqlSugar #endregion #region Common + public IUpdateable Clone() + { + this.Context.SugarActionType = SugarActionType.Update; + var result = InstanceFactory.GetUpdateableProvider(this.Context.CurrentConnectionConfig); + var sqlBuilder = InstanceFactory.GetSqlbuilder(this.Context.CurrentConnectionConfig); ; + result.Context = this.Context; + result.EntityInfo = this.Context.EntityMaintenance.GetEntityInfo(); + result.SqlBuilder = sqlBuilder; + result.SqlBuilder.Context = this.Context; + result.UpdateObjs = UpdateObjs; + result.WhereColumnList= this.WhereColumnList; + result.IsWhereColumns = this.IsWhereColumns; + result.IgnoreColumnNameList= this.IgnoreColumnNameList; + result.IsAs = this.IsAs; + result.IsOffIdentity= this.IsOffIdentity; + result.IsEnableDiffLogEvent= this.IsEnableDiffLogEvent; + result.diffModel = this.diffModel; + result.UpdateParameterIsNull= this.UpdateParameterIsNull; + result.RemoveCacheFunc= this.RemoveCacheFunc; + result.SetColumnsIndex = this.SetColumnsIndex; + result.OldMappingTableList= this.OldMappingTableList; + result.MappingColumnList= this.MappingColumnList; + result.columns = this.columns.ToList(); + result.UpdateBuilder = InstanceFactory.GetUpdateBuilder(this.Context.CurrentConnectionConfig); + result.UpdateBuilder.Builder = sqlBuilder; + result.UpdateBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.Context.CurrentConnectionConfig); + result.Context=this.Context; + result.UpdateBuilder.DbColumnInfoList = this.UpdateBuilder.DbColumnInfoList.ToList(); + result.UpdateBuilder.TableName= this.UpdateBuilder.TableName; + result.UpdateBuilder.WhereValues = this.UpdateBuilder.WhereValues.ToList(); + result.UpdateBuilder.Parameters= this.UpdateBuilder.Parameters.ToList(); + result.UpdateBuilder.IsListUpdate= this.UpdateBuilder.IsListUpdate; + result.UpdateBuilder.IsWhereColumns= this.UpdateBuilder.IsWhereColumns; + result.UpdateBuilder.WhereValues=this.UpdateBuilder.WhereValues.ToList(); + result.UpdateBuilder.IsNoUpdateDefaultValue = this.UpdateBuilder.IsNoUpdateDefaultValue; + result.UpdateBuilder.IsNoUpdateNull= this.UpdateBuilder.IsNoUpdateNull; + result.UpdateBuilder.SetValues= this.UpdateBuilder.SetValues.ToList(); + result.UpdateBuilder.Context = this.Context; + return result; + } public IUpdateable With(string lockString) { if (this.Context.CurrentConnectionConfig.DbType == DbType.SqlServer) @@ -227,7 +278,7 @@ namespace SqlSugar public IUpdateable IgnoreColumns(bool ignoreAllNullColumns, bool isOffIdentity = false, bool ignoreAllDefaultValue = false) { - Check.Exception(this.UpdateObjs.Count() > 1 && ignoreAllNullColumns, ErrorMessage.GetThrowMessage("ignoreNullColumn NoSupport batch insert", "ignoreNullColumn 不支持批量操作")); + //Check.Exception(this.UpdateObjs.Count() > 1 && ignoreAllNullColumns, ErrorMessage.GetThrowMessage("ignoreNullColumn NoSupport batch insert", "ignoreNullColumn 不支持批量操作")); UpdateBuilder.IsOffIdentity = isOffIdentity; if (this.UpdateBuilder.LambdaExpressions == null) this.UpdateBuilder.LambdaExpressions = InstanceFactory.GetLambdaExpressions(this.Context.CurrentConnectionConfig); diff --git a/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/ResolveItems/BaseResolve_Validate.cs b/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/ResolveItems/BaseResolve_Validate.cs index f603362b8..fe2524dae 100644 --- a/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/ResolveItems/BaseResolve_Validate.cs +++ b/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/ResolveItems/BaseResolve_Validate.cs @@ -14,7 +14,7 @@ namespace SqlSugar private static bool IsSubToList(Expression item) { - return ExpressionTool.GetMethodName(item) == "ToList" && IsSubquery(item); + return ExpressionTool.GetMethodName(item).IsIn("ToList","First") && IsSubquery(item); } private static bool IsSubquery(Expression item) diff --git a/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/Items/SubFirst.cs b/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/Items/SubFirst.cs new file mode 100644 index 000000000..43065de59 --- /dev/null +++ b/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/Items/SubFirst.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; +using System.Threading.Tasks; + +namespace SqlSugar +{ + public class SubFirst : ISubOperation + { + public bool HasWhere + { + get; set; + } + + public string Name + { + get + { + return "First"; + } + } + + public Expression Expression + { + get; set; + } + + + public int Sort + { + get + { + return 200; + } + } + + public ExpressionContext Context + { + get; set; + } + + public string GetValue(Expression expression = null) + { + ; + var exp = expression as MethodCallExpression; + InitType(exp); + var type = expression.Type; + if (type.FullName.IsCollectionsList() + && exp.Arguments.Count == 0 && type.GenericTypeArguments.Length > 0 + && this.Context.SugarContext != null + && this.Context.SugarContext.QueryBuilder.IsSelectNoAll) + { + var entity = type.GenericTypeArguments[0]; + var columnNames = this.Context.SugarContext.Context.EntityMaintenance.GetEntityInfo(entity).Columns; + var columnsString = string.Join(",", columnNames + .Where(it => it.IsIgnore == false) + .Where(it => it.DbColumnName.HasValue()) + .Select(it => this.Context.GetTranslationColumnName(it.DbColumnName))); + return $"{columnsString},@sugarIndex as sugarIndex"; + } + else if (exp.Arguments.Count == 0) + { + return "*,@sugarIndex as sugarIndex"; + } + var argExp = exp.Arguments[0]; + var parametres = (argExp as LambdaExpression).Parameters; + if ((argExp as LambdaExpression).Body is UnaryExpression) + { + argExp = ((argExp as LambdaExpression).Body as UnaryExpression).Operand; + } + var argLambda = argExp as LambdaExpression; + var copyContext = this.Context.GetCopyContextWithMapping(); + copyContext.Resolve(argLambda, ResolveExpressType.SelectMultiple); + var select = copyContext.Result.GetString(); + this.Context.Parameters.AddRange(copyContext.Parameters); + this.Context.Index = copyContext.Index; + this.Context.ParameterIndex = copyContext.ParameterIndex; + SetShortName(exp, null); + return select + ",@sugarIndex as sugarIndex"; + } + + private void InitType(MethodCallExpression exp) + { + if (exp.Arguments.Count > 0) + { + foreach (var arg in (exp.Arguments[0] as LambdaExpression).Parameters) + { + if (this.Context.InitMappingInfo != null) + { + this.Context.InitMappingInfo(arg.Type); + this.Context.RefreshMapping(); + } + } + } + } + + public void SetShortName(MethodCallExpression exp, string result) + { + if (exp.Arguments[0] is LambdaExpression) + { + var parameters = (exp.Arguments[0] as LambdaExpression).Parameters; + if (parameters != null && parameters.Count > 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters[0].ObjToString()); + } + + } + } + public void SetShortNameNext(MethodCallExpression exp, string result) + { + if (exp.Arguments.Count > 1 && exp.Arguments[1] is LambdaExpression) + { + var parameters = (exp.Arguments[1] as LambdaExpression).Parameters; + if (parameters != null && parameters.Count > 0) + { + this.Context.CurrentShortName = this.Context.GetTranslationColumnName(parameters[0].ObjToString()); + } + + } + } + } +} diff --git a/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/SubResolve.cs b/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/SubResolve.cs index a5ccfca4b..e326332b3 100644 --- a/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/SubResolve.cs +++ b/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/SubResolve.cs @@ -223,7 +223,7 @@ namespace SqlSugar }).ToList(); SetOrderByIndex(isubList); isubList.Insert(0, new SubBegin()); - if (isubList.Any(it => it is SubSelect)) + if (isubList.Any(it => it is SubSelect||it is SubFirst)) { isubList.Add(new SubTop() { Context = this.context }); } diff --git a/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/SubTools.cs b/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/SubTools.cs index 8ad4a2740..c8818cfcc 100644 --- a/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/SubTools.cs +++ b/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/SubTools.cs @@ -38,7 +38,8 @@ namespace SqlSugar new SubEnableTableFilter(){ Context=Context }, new SubSelectStringJoin{ Context=Context }, new SubDistinctCount{ Context=Context }, - new SubToList{ Context=Context} + new SubToList{ Context=Context}, + new SubFirst(){ Context=Context } }; } diff --git a/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/Subquerable.cs b/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/Subquerable.cs index d7520ad57..36d0c7e44 100644 --- a/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/Subquerable.cs +++ b/Src/Asp.NetCore2/SqlSugar/ExpressionsToSql/Subquery/Subquerable.cs @@ -180,5 +180,14 @@ namespace SqlSugar { return null; } + + public T First() + { + return default(T); + } + public TResult First(Func selector) where TResult : class, new() + { + return default(TResult); + } } } diff --git a/Src/Asp.NetCore2/SqlSugar/Interface/IUpdateable.cs b/Src/Asp.NetCore2/SqlSugar/Interface/IUpdateable.cs index 791eb691f..47026dd11 100644 --- a/Src/Asp.NetCore2/SqlSugar/Interface/IUpdateable.cs +++ b/Src/Asp.NetCore2/SqlSugar/Interface/IUpdateable.cs @@ -104,5 +104,6 @@ namespace SqlSugar SplitTableUpdateProvider SplitTable(Func, IEnumerable> getTableNamesFunc); SplitTableUpdateByObjectProvider SplitTable(); IUpdateable EnableQueryFilter(); + IUpdateable Clone(); } }