public override void VisitGroupJoinClause(GroupJoinClause groupJoinClause, QueryModel queryModel, int index)
        {
            JoinClause          joinClause                = (JoinClause)groupJoinClause.JoinClause;
            Type                tOuter                    = queryModel.MainFromClause.ItemType;
            Type                tInner                    = joinClause.ItemType;
            Type                tOuterKey                 = joinClause.OuterKeySelector.Type;
            Type                tInnerKey                 = joinClause.InnerKeySelector.Type;
            Type                tIEnumerableInner         = typeof(IEnumerable <>).MakeGenericType(tInner);
            Type                tResult                   = queryModel.SelectClause.Selector.Type;
            ParameterExpression iEnumerableInnerParameter = Expression.Parameter(tIEnumerableInner, "d");
            ParameterExpression outerParameter            = Expression.Parameter(tOuter, "p");
            ParameterExpression innerParameter            = Expression.Parameter(tInner, "d");
            ParameterExpression outerKeyParameter         = Expression.Parameter(tOuter, "p");
            Expression          outerKey                  = UpdateExpressionVisitor.Update(joinClause.OuterKeySelector, new[] { outerKeyParameter }, dbContext, queryContext);
            LambdaExpression    outerKeyLamda             = Expression.Lambda(outerKey, outerKeyParameter);
            ParameterExpression tInnerKeyParameter        = Expression.Parameter(tInner, "d");
            Expression          innerKey                  = UpdateExpressionVisitor.Update(joinClause.InnerKeySelector, new[] { tInnerKeyParameter }, dbContext, queryContext);
            LambdaExpression    innerKeyLamda             = Expression.Lambda(innerKey, tInnerKeyParameter);
            Expression          selector                  = UpdateExpressionVisitor.Update(queryModel.SelectClause.Selector, new[] { outerParameter, iEnumerableInnerParameter }, dbContext, queryContext);
            LambdaExpression    selectorLambda            = Expression.Lambda(selector, new[] { outerParameter, iEnumerableInnerParameter });
            MethodInfo          groupJoin                 = GetMethods("GroupJoin", expression.Type, 4).Where(p =>
                                                                                                              p.GetParameters().Count() == 5).Single().
                                                            MakeGenericMethod(new Type[] { tOuter, tInner, tOuterKey, tResult });

            expression            = Expression.Call(groupJoin, new[] { expression, joinClause.InnerSequence, outerKeyLamda, innerKeyLamda, selectorLambda });
            flagsNotVisitSelector = true;
        }
        public override void VisitSelectClause(SelectClause selectClause, QueryModel queryModel)
        {
            var genericTypeDefinition = expression.Type.GetGenericArguments();

            if (flagsNotVisitSelector || (genericTypeDefinition.Count() == 1 && genericTypeDefinition.First().Equals(selectClause.Selector.Type)))
            {
                return;
            }

            Type sourceType = genericTypeDefinition.Count() == 1 ? genericTypeDefinition.First() : resultOptionType;

            MethodInfo selectMethodInfo = typeof(IQueryable).IsAssignableFrom(expression.Type)
                ? QueryableMethodsHelper.Select
                : EnumerableMethodsHelper.Select;

            MemberExpression memberExpression = selectClause.Selector as MemberExpression;

            if (memberExpression != null)
            {
                var parameter = Expression.Parameter(memberExpression.Expression.Type, "p");
                memberExpression = (MemberExpression)UpdateExpressionVisitor.Update(memberExpression, new[] { parameter }, dbContext, queryContext);
                var lambda = Expression.Lambda(memberExpression, parameter);
                var genericMethodSelect = selectMethodInfo.MakeGenericMethod(memberExpression.Expression.Type, memberExpression.Type);
                var call = Expression.Call(genericMethodSelect, expression, lambda);
                expression = call;
                return;
            }
            NewExpression newExpression = selectClause.Selector as NewExpression;

            if (newExpression != null)
            {
                if (sourceType != null)
                {
                    var parameter = Expression.Parameter(sourceType, "p");
                    List <Expression> updateArgExp = new List <Expression>();
                    foreach (var argument in newExpression.Arguments)
                    {
                        var update = UpdateExpressionVisitor.Update(argument, new[] { parameter }, dbContext, queryContext);
                        updateArgExp.Add(update);
                    }
                    Expression newResult           = Expression.New(newExpression.Constructor, updateArgExp);
                    var        selectGenericMethod = selectMethodInfo.MakeGenericMethod(sourceType, newResult.Type);
                    var        lambda = Expression.Lambda(newResult, parameter);
                    var        call   = Expression.Call(selectGenericMethod, expression, lambda);
                    expression = call;
                    return;
                }
            }
            if (sourceType != null)
            {
                var parameter           = Expression.Parameter(sourceType, "p");
                var update              = UpdateExpressionVisitor.Update(selectClause.Selector, new[] { parameter }, dbContext, queryContext);
                var selectGenericMethod = selectMethodInfo.MakeGenericMethod(sourceType, selectClause.Selector.Type);
                var lambda              = Expression.Lambda(update, parameter);
                var call = Expression.Call(selectGenericMethod, expression, lambda);
                expression = call;
                return;
            }
            throw new NotImplementedException();
        }
        public override void VisitAdditionalFromClause(AdditionalFromClause fromClause, QueryModel queryModel, int index)
        {
            Type typeParameter                   = GetTypeParameter(fromClause.FromExpression);
            ParameterExpression parameter        = Expression.Parameter(typeParameter, "p");
            Expression          updateExpression = UpdateExpressionVisitor.Update(fromClause.FromExpression, new[] { parameter }, dbContext, queryContext);

            updateExpression = Expression.Convert(updateExpression, typeof(IEnumerable <>).MakeGenericType(updateExpression.Type.GetGenericArguments()));
            MethodInfo           genericSelectMany = QueryableMethodsHelper.SelectMany.MakeGenericMethod(typeParameter, fromClause.ItemType);
            LambdaExpression     lambda            = Expression.Lambda(updateExpression, parameter);
            MethodCallExpression selectManyResult  = Expression.Call(genericSelectMany, new[] { expression, lambda });

            expression = selectManyResult;
        }
        public override void VisitWhereClause(WhereClause whereClause, QueryModel queryModel, int index)
        {
            ParameterExpression     parameterExpression     = Expression.Parameter(entityType, "p");
            UpdateExpressionVisitor updateExpressionVisitor = new UpdateExpressionVisitor(new[] { parameterExpression }, dbContext, queryContext);
            Expression predicateUpdate = updateExpressionVisitor.Visit(whereClause.Predicate);

            if (updateExpressionVisitor.MainParametrExpression != null)
            {
                parameterExpression = updateExpressionVisitor.MainParametrExpression;
            }
            ;

            Expression whereLamda = Expression.Lambda(predicateUpdate, parameterExpression);

            MethodInfo where = GetMethods("Where", expression.Type, 1).First().MakeGenericMethod(entityType);
            expression       = Expression.Call(where, new[] { expression, whereLamda });
        }
        //public override void VisitOrderByClause(OrderByClause orderByClause, QueryModel queryModel, int index) {
        //    throw new NotSupportedException();
        //}

        public override void VisitOrdering(Ordering ordering, QueryModel queryModel, OrderByClause orderByClause, int index)
        {
            ParameterExpression expressionParam     = Expression.Parameter(entityType, "p");
            Expression          updateExpression    = UpdateExpressionVisitor.Update(ordering.Expression, new[] { expressionParam }, dbContext, queryContext);
            LambdaExpression    expressionKeyLambda = Expression.Lambda(updateExpression, expressionParam);

            if (ordering.OrderingDirection == OrderingDirection.Asc)
            {
                MethodInfo orderBy = GetMethod("OrderBy", expression.Type, 1).MakeGenericMethod(entityType, ordering.Expression.Type);
                expression = Expression.Call(orderBy, new[] { expression, expressionKeyLambda });
            }
            else
            {
                MethodInfo orderByDescending = GetMethod("OrderByDescending", expression.Type, 1).MakeGenericMethod(entityType, ordering.Expression.Type);
                expression = Expression.Call(orderByDescending, new[] { expression, expressionKeyLambda });
            }
        }
        public override void VisitMainFromClause(MainFromClause fromClause, QueryModel queryModel)
        {
            bool processed = false;

            expression = queryModel.MainFromClause.FromExpression;
            if (expression is SubQueryExpression)
            {
                QueryModel subQueryModel = ((SubQueryExpression)expression).QueryModel;
                VisitQueryModel(subQueryModel);
                processed = true;
            }
            if (expression is ConstantExpression)
            {
                expression = Utility.DbContextHelper.GetDbSet(dbContext, (ConstantExpression)expression);
                if (BaseSourceExpressionCreated != null)
                {
                    BaseSourceExpressionCreatedEventArgs eventArgs = new BaseSourceExpressionCreatedEventArgs(expression, queryModel.MainFromClause.ItemType);
                    BaseSourceExpressionCreated(this, eventArgs);
                    expression = eventArgs.Expression;
                }
                processed = true;
            }
            if (expression is MemberExpression)
            {
                QuerySourceReferenceExpression querySourceReferenceExpression = (expression as MemberExpression)?.Expression as QuerySourceReferenceExpression;
                if (querySourceReferenceExpression != null)
                {
                    Type   paramType = querySourceReferenceExpression.ReferencedQuerySource.ItemType;
                    string paramName = querySourceReferenceExpression.ReferencedQuerySource.ItemName;
                    MainParamerExpression = Expression.Parameter(paramType, paramName);
                    expression            = UpdateExpressionVisitor.Update(expression, new[] { MainParamerExpression }, dbContext, queryContext);
                    processed             = true;
                }
            }
            if (expression is ParameterExpression)
            {
                processed = true; //TODO?
            }
            if (!processed)
            {
                throw new NotImplementedException();
            }
            entityType   = queryModel.MainFromClause.ItemType;
            selectorType = queryModel.SelectClause.Selector.Type;
        }
Ejemplo n.º 7
0
        public IQueryCommand <TResult> Select <TResult>(Expression <Func <T, TResult> > predicate)
        {
            var visitor = new UpdateExpressionVisitor();

            visitor.Visit(predicate);

            visitor.UpdatedFields.ForEach(t =>
            {
                _selectField.Append($"{t.Prefix}.{t.ColumnName} as {t.Parameter},");

                prefix = t.Prefix;
            });
            _selectField.Remove(_selectField.Length - 1, 1);
            return(new SimpleQueryable <TResult>(_connection, Build(), _sqlPamater)
            {
                Aop = Aop
            });
        }
Ejemplo n.º 8
0
        // 创建 UPDATE 命令
        protected override Command ParseUpdateCommand <T>(DbQueryableInfo_Update <T> uQueryInfo, ParserToken token)
        {
            ISqlBuilder builder     = this.CreateSqlBuilder(token);
            var         typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo <T>();

            builder.Append("UPDATE t0 SET");
            builder.AppendNewLine();

            if (uQueryInfo.Entity != null)
            {
                object      entity       = uQueryInfo.Entity;
                ISqlBuilder whereBuilder = this.CreateSqlBuilder(token);
                bool        useKey       = false;
                int         length       = 0;

                foreach (var kv in typeRuntime.Invokers)
                {
                    MemberInvokerBase invoker = kv.Value;
                    var column = invoker.Column;
                    if (column != null && column.IsIdentity)
                    {
                        goto gotoLabel;                                      // fix issue# 自增列同时又是主键
                    }
                    if (column != null && column.NoMapped)
                    {
                        continue;
                    }
                    if (invoker.Column != null && column.DbType is SqlDbType && (SqlDbType)column.DbType == SqlDbType.Timestamp)
                    {
                        continue;                                                                                                          // 行版本号
                    }
                    if (invoker.ForeignKey != null)
                    {
                        continue;
                    }
                    if (invoker.Member.MemberType == System.Reflection.MemberTypes.Method)
                    {
                        continue;
                    }

                    builder.AppendMember("t0", invoker.Member.Name);
                    builder.Append(" = ");

gotoLabel:
                    var value = invoker.Invoke(entity);
                    var seg = builder.GetSqlValueWidthDefault(value, column);

                    if (column == null || !column.IsIdentity)
                    {
                        builder.Append(seg);
                        length = builder.Length;
                        builder.Append(',');
                        builder.AppendNewLine();
                    }

                    if (column != null && column.IsKey)
                    {
                        useKey = true;
                        whereBuilder.AppendMember("t0", invoker.Member.Name);
                        whereBuilder.Append(" = ");
                        whereBuilder.Append(seg);
                        whereBuilder.Append(" AND ");
                    }
                }

                if (!useKey)
                {
                    throw new XFrameworkException("Update<T>(T value) require T must have key column.");
                }

                builder.Length       = length;
                whereBuilder.Length -= 5;

                builder.AppendNewLine();
                builder.Append("FROM ");
                builder.AppendMember(typeRuntime.TableName, !typeRuntime.IsTemporary);
                builder.Append(" t0");


                builder.AppendNewLine();
                builder.Append("WHERE ");
                builder.Append(whereBuilder);
            }
            else if (uQueryInfo.Expression != null)
            {
                TableAliasCache       aliases = this.PrepareAlias <T>(uQueryInfo.SelectInfo, token);
                ExpressionVisitorBase visitor = null;
                visitor = new UpdateExpressionVisitor(this, aliases, uQueryInfo.Expression);
                visitor.Write(builder);

                builder.AppendNewLine();
                builder.Append("FROM ");
                builder.AppendMember(typeRuntime.TableName, !typeRuntime.IsTemporary);
                builder.AppendAs("t0");

                var cmd2 = new SelectCommand(this, aliases, token)
                {
                    HasManyNavigation = uQueryInfo.SelectInfo.HasManyNavigation
                };

                visitor = new JoinExpressionVisitor(this, aliases, uQueryInfo.SelectInfo.Joins);
                visitor.Write(cmd2.JoinFragment);

                visitor = new WhereExpressionVisitor(this, aliases, uQueryInfo.SelectInfo.WhereExpression);
                visitor.Write(cmd2.WhereFragment);
                cmd2.AddNavMembers(visitor.NavMembers);

                builder.Append(cmd2.CommandText);
            }

            return(new Command(builder.ToString(), builder.Token != null ? builder.Token.Parameters : null, System.Data.CommandType.Text));
        }
        /// <summary>
        /// 创建 UPDATE 命令
        /// </summary>
        /// <param name="dbQuery">查询语义</param>
        /// <param name="token">解析上下文</param>
        /// <returns></returns>
        protected override RawCommand ResolveUpdateCommand <T>(IDbQueryableInfo_Update dbQuery, ResolveToken token)
        {
            var         context     = (SqlServerDbContext)token.DbContext;
            ISqlBuilder builder     = this.CreateSqlBuilder(token);
            var         typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo <T>();

            builder.Append("UPDATE t0 SET");
            builder.AppendNewLine();

            if (dbQuery.Entity != null)
            {
                object      entity    = dbQuery.Entity;
                ISqlBuilder seg_Where = this.CreateSqlBuilder(token);
                bool        useKey    = false;
                int         length    = 0;

                foreach (var m in typeRuntime.Members)
                {
                    var column = m.Column;
                    if (column != null && column.IsIdentity)
                    {
                        goto gotoLabel;                                      // Fix issue# 自增列同时又是主键
                    }
                    if (column != null && column.NoMapped)
                    {
                        continue;
                    }
                    if (m.Column != null && column.DbType is SqlDbType && (SqlDbType)column.DbType == SqlDbType.Timestamp)
                    {
                        continue;                                                                                                    // 行版本号
                    }
                    if (m.ForeignKey != null)
                    {
                        continue;
                    }
                    if (m.Member.MemberType == System.Reflection.MemberTypes.Method)
                    {
                        continue;
                    }

                    builder.AppendMember("t0", m.Member.Name);
                    builder.Append(" = ");

gotoLabel:
                    var value = m.Invoke(entity);
                    var seg = this.DbValue.GetSqlValueWidthDefault(value, token, column);

                    if (column == null || !column.IsIdentity)
                    {
                        builder.Append(seg);
                        length = builder.Length;
                        builder.Append(',');
                        builder.AppendNewLine();
                    }

                    if (column != null && column.IsKey)
                    {
                        useKey = true;
                        seg_Where.AppendMember("t0", m.Member.Name);
                        seg_Where.Append(" = ");
                        seg_Where.Append(seg);
                        seg_Where.Append(" AND ");
                    }
                }

                if (!useKey)
                {
                    throw new XFrameworkException("Update<T>(T value) require entity must have key column.");
                }

                builder.Length    = length;
                seg_Where.Length -= 5;

                builder.AppendNewLine();
                builder.Append("FROM ");
                builder.AppendMember(typeRuntime.TableName, !typeRuntime.IsTemporary);
                builder.Append(" t0");

                builder.AppendNewLine();
                builder.Append("WHERE ");
                builder.Append(seg_Where);
            }
            else if (dbQuery.Expression != null)
            {
                TableAlias            aliases = this.PrepareTableAlias(dbQuery.Query, token);
                ExpressionVisitorBase visitor = null;
                visitor = new UpdateExpressionVisitor(this, aliases, dbQuery.Expression);
                visitor.Write(builder);

                builder.AppendNewLine();
                builder.Append("FROM ");
                builder.AppendMember(typeRuntime.TableName, !typeRuntime.IsTemporary);
                builder.AppendAs("t0");

                var cmd = new SqlServerMappingCommand(context, aliases, token)
                {
                    HasMany = dbQuery.Query.HasMany
                };

                visitor = new SqlServerJoinExpressionVisitor(context, aliases, dbQuery.Query.Joins);
                visitor.Write(cmd.JoinFragment);

                visitor = new WhereExpressionVisitor(this, aliases, dbQuery.Query.Where);
                visitor.Write(cmd.WhereFragment);
                cmd.AddNavMembers(visitor.NavMembers);

                builder.Append(cmd.CommandText);
            }

            return(new RawCommand(builder.ToString(), builder.Token != null ? builder.Token.Parameters : null, System.Data.CommandType.Text));
        }
        public override void VisitResultOperator(ResultOperatorBase resultOperator, QueryModel queryModel, int index)
        {
            resultOptionType = resultOptionType ?? queryModel.SelectClause.Selector.Type;
            if (resultOperator is CastResultOperator)
            {
                CastResultOperator castResultOperator = (CastResultOperator)resultOperator;
                Type       castType = castResultOperator.CastItemType;
                MethodInfo cast     = GetMethod("Cast", expression.Type).MakeGenericMethod(castType);
                resultOptionType = castType;
                expression       = Expression.Call(cast, expression);
                return;
            }
            if (resultOperator is AllResultOperator)
            {
                AllResultOperator   allResultOperator   = (AllResultOperator)resultOperator;
                ParameterExpression parameterExpression = Expression.Parameter(selectorType, "p");
                Expression          predicateUpdate     = UpdateExpressionVisitor.Update(allResultOperator.Predicate, new[] { parameterExpression }, dbContext, queryContext);
                Expression          allLamda            = Expression.Lambda(predicateUpdate, parameterExpression);
                MethodInfo          all = GetMethod("All", expression.Type, 1).MakeGenericMethod(selectorType);
                expression = Expression.Call(all, new[] { expression, allLamda });
                return;
            }
            if (resultOperator is FirstResultOperator)
            {
                FirstResultOperator firstResultOperator = (FirstResultOperator)resultOperator;
                if (firstResultOperator.ReturnDefaultWhenEmpty)
                {
                    MethodInfo firstOrDefault = GetMethod("FirstOrDefault", expression.Type, 0).MakeGenericMethod(selectorType);
                    expression = Expression.Call(firstOrDefault, new[] { expression });
                }
                else
                {
                    MethodInfo first = GetMethod("First", expression.Type, 0).MakeGenericMethod(selectorType);
                    expression = Expression.Call(first, new[] { expression });
                }
                return;
            }
            if (resultOperator is SingleResultOperator)
            {
                SingleResultOperator singleResultOperator = (SingleResultOperator)resultOperator;
                if (singleResultOperator.ReturnDefaultWhenEmpty)
                {
                    MethodInfo firstOrDefault = GetMethod("SingleOrDefault", expression.Type, 0).MakeGenericMethod(selectorType);
                    expression = Expression.Call(firstOrDefault, new[] { expression });
                }
                else
                {
                    MethodInfo first = GetMethod("Single", expression.Type, 0).MakeGenericMethod(selectorType);
                    expression = Expression.Call(first, new[] { expression });
                }
                return;
            }
            if (resultOperator is AnyResultOperator)
            {
                MethodInfo any = GetMethod("Any", expression.Type).MakeGenericMethod(selectorType);
                expression = Expression.Call(any, new[] { expression });
                return;
            }
            if (resultOperator is TrackingResultOperator)
            {
                MethodInfo asNoTracking = typeof(EntityFrameworkQueryableExtensions).GetMethod("AsNoTracking").MakeGenericMethod(selectorType);
                expression = Expression.Call(asNoTracking, new[] { expression });
                return;
            }
            if (resultOperator is CountResultOperator)
            {
                MethodInfo count = GetMethod("Count", expression.Type).MakeGenericMethod(selectorType);
                expression = Expression.Call(count, new[] { expression });
                return;
            }
            if (resultOperator is AverageResultOperator)
            {
                MethodInfo average = GetAgregateMethod("Average", expression.Type, selectorType);
                expression = Expression.Call(average, new[] { expression });
                return;
            }
            if (resultOperator is ContainsResultOperator)
            {
                ContainsResultOperator containsResultOperator = (ContainsResultOperator)resultOperator;
                Expression             valExp;
                object value;
                ParameterExpression paramExp = containsResultOperator.Item as ParameterExpression;
                if (paramExp != null &&
                    queryContext.ParameterValues.TryGetValue(paramExp.Name, out value))
                {
                    valExp = Expression.Constant(value);
                }
                else
                {
                    valExp = containsResultOperator.Item;
                }
                if (containsResultOperator.Item is SubQueryExpression)
                {
                    SubQueryExpression subQueryExpression = (SubQueryExpression)containsResultOperator.Item;
                    QueryModelVisitor  queryModelVisitor  = new QueryModelVisitor(dbContext, queryContext);
                    queryModelVisitor.VisitQueryModel(subQueryExpression.QueryModel);
                    valExp = queryModelVisitor.expression;
                }

                MethodInfo contains = GetMethod("Contains", expression.Type, 1).MakeGenericMethod(selectorType);
                expression = Expression.Call(contains, new[] { expression, valExp });
                return;
            }
            if (resultOperator is DefaultIfEmptyResultOperator)
            {
                DefaultIfEmptyResultOperator defaultIfEmptyResultOperator = (DefaultIfEmptyResultOperator)resultOperator;
                MethodInfo defaultIfEmpty;
                if (defaultIfEmptyResultOperator.OptionalDefaultValue != null)
                {
                    defaultIfEmpty = GetMethod("DefaultIfEmpty", expression.Type, 1).MakeGenericMethod(selectorType);
                    expression     = Expression.Call(defaultIfEmpty, new[] { expression, defaultIfEmptyResultOperator.OptionalDefaultValue });
                }
                defaultIfEmpty = GetMethod("DefaultIfEmpty", expression.Type).MakeGenericMethod(selectorType);
                expression     = Expression.Call(defaultIfEmpty, expression);
                return;
            }
            if (resultOperator is DistinctResultOperator)
            {
                MethodInfo distinct = GetMethod("Distinct", expression.Type).MakeGenericMethod(selectorType);
                expression = Expression.Call(distinct, expression);
                return;
            }
            if (resultOperator is GroupResultOperator)
            {
                GroupResultOperator groupResultOperator = (GroupResultOperator)resultOperator;
                Type keySelectorType = GetTypeParameter(groupResultOperator.KeySelector);
                ParameterExpression keyExpressionParam  = Expression.Parameter(keySelectorType, "p");
                Expression          keyExpression       = UpdateExpressionVisitor.Update(groupResultOperator.KeySelector, new[] { keyExpressionParam }, dbContext, queryContext);
                LambdaExpression    keyLambdaExpression = Expression.Lambda(keyExpression, keyExpressionParam);
                Type elementSelectorType = GetTypeParameter(groupResultOperator.ElementSelector);
                ParameterExpression ElementExpressionParam  = Expression.Parameter(elementSelectorType, "p");
                Expression          ElementExpression       = UpdateExpressionVisitor.Update(groupResultOperator.ElementSelector, new[] { ElementExpressionParam }, dbContext, queryContext);
                LambdaExpression    ElementLambdaExpression = Expression.Lambda(ElementExpression, ElementExpressionParam);
                Type       tSource  = queryModel.MainFromClause.ItemType;
                Type       tKey     = keyExpression.Type;
                Type       tElement = ElementExpression.Type;
                Type       tResult  = queryModel.ResultTypeOverride;
                MethodInfo groupBy  = GetMethods("GroupBy", expression.Type, 2).Where(p => p.GetParameters()[2].Name == "elementSelector").Single().
                                      MakeGenericMethod(tSource, tKey, tElement);
                expression = Expression.Call(groupBy, new[] { expression, keyLambdaExpression, ElementLambdaExpression });
                return;
            }
            if (resultOperator is LastResultOperator)
            {
                LastResultOperator lastResultOperator = (LastResultOperator)resultOperator;
                if (lastResultOperator.ReturnDefaultWhenEmpty)
                {
                    MethodInfo lastOrDefault = GetMethod("LastOrDefault", expression.Type, 0).MakeGenericMethod(selectorType);
                    expression = Expression.Call(lastOrDefault, new[] { expression });
                }
                else
                {
                    MethodInfo last = GetMethod("Last", expression.Type, 0).MakeGenericMethod(selectorType);
                    expression = Expression.Call(last, new[] { expression });
                }
                return;
            }
            if (resultOperator is LongCountResultOperator)
            {
                MethodInfo longCount = GetMethod("LongCount", expression.Type).MakeGenericMethod(selectorType);
                expression = Expression.Call(longCount, new[] { expression });
                return;
            }
            if (resultOperator is MaxResultOperator)
            {
                MethodInfo max = GetMethod("Max", expression.Type).MakeGenericMethod(selectorType);
                expression = Expression.Call(max, expression);
                return;
            }
            if (resultOperator is MinResultOperator)
            {
                MethodInfo min = GetMethod("Min", expression.Type).MakeGenericMethod(selectorType);
                expression = Expression.Call(min, expression);
                return;
            }
            if (resultOperator is SumResultOperator)
            {
                MethodInfo sum = GetAgregateMethod("Sum", expression.Type, selectorType);
                expression = Expression.Call(sum, expression);
                return;
            }
            if (resultOperator is SkipResultOperator)
            {
                SkipResultOperator  skipResultOperator = (SkipResultOperator)resultOperator;
                Expression          expVal;
                object              value;
                ParameterExpression paramExp = skipResultOperator.Count as ParameterExpression;
                if (paramExp != null &&
                    queryContext.ParameterValues.TryGetValue(paramExp.Name, out value))
                {
                    expVal = Expression.Constant(value);
                }
                else
                {
                    expVal = skipResultOperator.Count;
                }
                MethodInfo skip = GetMethod("Skip", expression.Type, 1).MakeGenericMethod(selectorType);
                expression = Expression.Call(skip, new[] { expression, expVal });
                return;
            }
            if (resultOperator is TakeResultOperator)
            {
                TakeResultOperator  takeResultOperator = (TakeResultOperator)resultOperator;
                Expression          expVal;
                object              value;
                ParameterExpression paramExp = takeResultOperator.Count as ParameterExpression;
                if (paramExp != null &&
                    queryContext.ParameterValues.TryGetValue(paramExp.Name, out value))
                {
                    expVal = Expression.Constant(value);
                }
                else
                {
                    expVal = takeResultOperator.Count;
                }
                MethodInfo take = GetMethod("Take", expression.Type, 1).MakeGenericMethod(selectorType);
                expression = Expression.Call(take, new[] { expression, expVal });
                return;
            }
            if (resultOperator is IncludeResultOperator)
            {
                IncludeResultOperator includeResultOperator = (IncludeResultOperator)resultOperator;
                Expression            includeExpression     = includeResultOperator.NavigationPropertyPath;
                Type paramExpressionType = null;
                ParameterExpression parameterExpression = null;
                if (includeExpression is MemberExpression)
                {
                    MemberExpression memberExpression = (MemberExpression)includeExpression;
                    paramExpressionType = memberExpression.Expression.Type;
                    parameterExpression = Expression.Parameter(paramExpressionType, "p");
                    includeExpression   = Expression.Property(parameterExpression, memberExpression.Member.Name);
                }
                else
                {
                    paramExpressionType = GetTypeParameter(includeExpression);
                    parameterExpression = Expression.Parameter(paramExpressionType, "p");
                }
                Expression       updateOuterExpression   = UpdateExpressionVisitor.Update(includeExpression, new[] { parameterExpression }, dbContext, queryContext);
                LambdaExpression lambdaIncludeExpression = Expression.Lambda(updateOuterExpression, parameterExpression);
                MethodInfo       include = typeof(EntityFrameworkQueryableExtensions).GetMethods().First(m => m.Name == "Include").MakeGenericMethod(selectorType, updateOuterExpression.Type);
                expression = Expression.Call(include, new[] { expression, lambdaIncludeExpression });

                if (includeResultOperator.ChainedNavigationProperties != null)
                {
                    foreach (PropertyInfo propertyInfo in includeResultOperator.ChainedNavigationProperties)
                    {
                        Type       propertyType = propertyInfo.PropertyType;
                        Type       argument     = expression.Type.GetGenericArguments().Last();
                        MethodInfo thenInclude;
                        Type       realType;
                        if (typeof(IEnumerable).IsAssignableFrom(argument))
                        {
                            realType    = argument.GetGenericArguments().First();
                            thenInclude = ThenIncludeCollection.MakeGenericMethod(includeResultOperator.QuerySource.ItemType, realType, propertyType);
                        }
                        else
                        {
                            realType    = argument;
                            thenInclude = ThenIncludeProperty.MakeGenericMethod(includeResultOperator.QuerySource.ItemType, realType, propertyType);
                        }
                        ParameterExpression parameterThenIncludeExpression = Expression.Parameter(realType, "p");
                        MemberExpression    property = Expression.Property(parameterThenIncludeExpression, propertyInfo);
                        LambdaExpression    lambdaThenIncludeExpression = Expression.Lambda(property, parameterThenIncludeExpression);
                        expression = Expression.Call(thenInclude, new[] { expression, lambdaThenIncludeExpression });
                    }
                }
                return;
            }
            if (resultOperator is OfTypeResultOperator)
            {
                OfTypeResultOperator ofTypeResultOperator = (OfTypeResultOperator)resultOperator;
                selectorType = ofTypeResultOperator.SearchedItemType;
                var miOfType = GetMethod("OfType", expression.Type).MakeGenericMethod(ofTypeResultOperator.SearchedItemType);
                expression = Expression.Call(miOfType, new[] { expression });
                return;
            }
            throw new NotSupportedException();
        }
Ejemplo n.º 11
0
        /// <summary>
        /// 创建 UPDATE 命令
        /// </summary>
        /// <param name="dbQuery">查询语义</param>
        /// <param name="token">解析上下文</param>
        /// <returns></returns>
        protected override RawCommand ResolveUpdateCommand <T>(IDbQueryableInfo_Update dbQuery, ResolveToken token)
        {
            ISqlBuilder builder     = this.CreateSqlBuilder(token);
            var         typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo <T>();

            builder.Append("UPDATE ");
            builder.AppendMember(typeRuntime.TableName, !typeRuntime.IsTemporary);
            builder.Append(" t0 SET");
            builder.AppendNewLine();

            if (dbQuery.Entity != null)
            {
                object      entity    = dbQuery.Entity;
                ISqlBuilder seg_Where = this.CreateSqlBuilder(token);
                bool        useKey    = false;
                int         length    = 0;


                foreach (var m in typeRuntime.Members)
                {
                    var column = m.Column;
                    if (column != null && column.IsIdentity)
                    {
                        goto gotoLabel;                                      // Fix issue# 自增列同时又是主键
                    }
                    if (column != null && column.NoMapped)
                    {
                        continue;
                    }
                    if (m.ForeignKey != null)
                    {
                        continue;
                    }
                    if (m.Member.MemberType == System.Reflection.MemberTypes.Method)
                    {
                        continue;
                    }

                    builder.AppendMember(m.Member.Name);
                    builder.Append(" = ");

gotoLabel:

                    if (column == null || !column.IsIdentity)
                    {
                        var value = m.Invoke(entity);
                        var seg   = this.DbValue.GetSqlValueWidthDefault(value, token, column);

                        builder.Append(seg);
                        length = builder.Length;
                        builder.Append(',');
                        builder.AppendNewLine();
                    }

                    if (column != null && column.IsKey)
                    {
                        useKey = true;
                    }
                }

                if (!useKey)
                {
                    throw new XFrameworkException("Update<T>(T value) require T must have key column.");
                }


                // ORACLE 需要注意参数顺序问题
                int index = -1;
                foreach (var m in typeRuntime.KeyMembers)
                {
                    var column = m.Column;
                    var value  = m.Invoke(entity);
                    var seg    = this.DbValue.GetSqlValueWidthDefault(value, token, column);
                    index += 1;

                    seg_Where.AppendMember(m.Member.Name);
                    seg_Where.Append(" = ");
                    seg_Where.Append(seg);
                    if (index < typeRuntime.KeyMembers.Count - 1)
                    {
                        seg_Where.Append(" AND ");
                    }
                }

                builder.Length = length;
                builder.AppendNewLine();
                builder.Append("WHERE ");
                builder.Append(seg_Where);
            }
            else if (dbQuery.Expression != null)
            {
                // SELECT 表达式
                LambdaExpression lambda = dbQuery.Expression as LambdaExpression;
                var        body         = lambda.Body;
                Expression expression   = null;
                if (body.NodeType == ExpressionType.MemberInit)
                {
                    var @init    = body as MemberInitExpression;
                    var bindings = new List <MemberBinding>(@init.Bindings);
                    foreach (var m in typeRuntime.KeyMembers)
                    {
                        var member  = Expression.MakeMemberAccess(lambda.Parameters[0], m.Member);
                        var binding = Expression.Bind(m.Member, member);
                        if (!bindings.Any(x => x.Member == m.Member))
                        {
                            bindings.Add(binding);
                        }
                    }
                    expression = Expression.MemberInit(@init.NewExpression, bindings);
                }
                else if (body.NodeType == ExpressionType.New)
                {
                    var newExpression = body as NewExpression;
                    var bindings      = new List <MemberBinding>();
                    for (int i = 0; i < newExpression.Members.Count; i++)
                    {
                        var m       = typeRuntime.GetMember(newExpression.Members[i].Name);
                        var binding = Expression.Bind(m.Member, newExpression.Arguments[i].Type != m.DataType
                            ? Expression.Convert(newExpression.Arguments[i], m.DataType)
                            : newExpression.Arguments[i]);
                        bindings.Add(binding);
                    }

                    foreach (var m in typeRuntime.KeyMembers)
                    {
                        var member  = Expression.MakeMemberAccess(lambda.Parameters[0], m.Member);
                        var binding = Expression.Bind(m.Member, member);
                        if (!bindings.Any(x => x.Member == m.Member))
                        {
                            bindings.Add(binding);
                        }
                    }

                    var newExpression2 = Expression.New(typeRuntime.Constructor.Constructor);
                    expression = Expression.MemberInit(newExpression2, bindings);
                }

                // 解析查询以确定是否需要嵌套
                dbQuery.Query.Select = new DbExpression(DbExpressionType.Select, expression);
                var cmd = (MappingCommand)this.ResolveSelectCommand(dbQuery.Query, 1, false, null);//, token);

                if ((cmd.NavMembers != null && cmd.NavMembers.Count > 0) || dbQuery.Query.Joins.Count > 0)
                {
                    // 无法使用 DISTINCT, GROUP BY 等子句从视图中选择 ROWID 或采样。UPDATE 不能用rowid
                    // 有导航属性或者关联查询,使用 MERGE INTO 语法。要求必须有主键

                    if (typeRuntime.KeyMembers == null || typeRuntime.KeyMembers.Count == 0)
                    {
                        throw new XFrameworkException("Update<T>(Expression<Func<T, object>> updateExpression) require entity must have key column.");
                    }

                    builder.Length = 0;
                    builder.Append("MERGE INTO ");
                    builder.AppendMember(typeRuntime.TableName, !typeRuntime.IsTemporary);
                    builder.AppendNewLine(" t0");
                    builder.Append("USING (");

                    cmd = (MappingCommand)this.ResolveSelectCommand(dbQuery.Query, 1, false, token);
                    builder.AppendNewLine(cmd.CommandText);
                    builder.Append(") t1 ON (");
                    foreach (var m in typeRuntime.KeyMembers)
                    {
                        builder.AppendMember("t0", m.Name);
                        builder.Append(" = ");
                        builder.AppendMember("t1", m.Name);
                        builder.Append(" AND ");
                    }
                    builder.Length -= 5;
                    builder.Append(')');

                    // UPDATE
                    builder.AppendNewLine();
                    builder.AppendNewLine("WHEN MATCHED THEN UPDATE SET ");

                    // SET 字段
                    var visitor = new OracleUpdateExpressionVisitor(this, null, dbQuery.Expression);
                    visitor.Write(builder);
                }
                else
                {
                    // 直接 SQL 的 UPDATE 语法
                    TableAlias aliases = this.PrepareTableAlias(dbQuery.Query, token);
                    var        visitor = new UpdateExpressionVisitor(this, aliases, dbQuery.Expression);
                    visitor.Write(builder);

                    var visitor_ = new WhereExpressionVisitor(this, aliases, dbQuery.Query.Where);
                    visitor_.Write(builder);
                }
            }

            builder.Append(';');
            return(new RawCommand(builder.ToString(), builder.Token != null ? builder.Token.Parameters : null, System.Data.CommandType.Text));
        }
Ejemplo n.º 12
0
        /// <summary>
        /// 创建 UPDATE 命令
        /// </summary>
        /// <param name="tree">查询语义</param>
        /// <param name="context">解析SQL命令上下文</param>
        /// <returns></returns>
        protected override DbRawCommand TranslateUpdateCommand <T>(DbQueryUpdateTree tree, ITranslateContext context)
        {
            ISqlBuilder builder     = this.CreateSqlBuilder(context);
            var         typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo <T>();

            builder.Append("UPDATE ");
            builder.AppendTable(typeRuntime.TableSchema, typeRuntime.TableName, typeRuntime.IsTemporary);
            builder.Append(" t0");

            if (tree.Entity != null)
            {
                object      entity       = tree.Entity;
                ISqlBuilder whereBuilder = this.CreateSqlBuilder(context);
                bool        useKey       = false;
                int         length       = 0;
                builder.AppendNewLine(" SET");

                foreach (var item in typeRuntime.Members)
                {
                    var m = item as FieldAccessorBase;
                    if (m == null || !m.IsDbField)
                    {
                        continue;
                    }
                    if (m.Column != null && m.Column.IsIdentity)
                    {
                        goto LABEL;                                          // Fix issue# 自增列同时又是主键
                    }
                    builder.AppendMember("t0", m.Member, typeRuntime.Type);
                    builder.Append(" = ");

LABEL:
                    var value = m.Invoke(entity);
                    var seg = this.Constor.GetSqlValueWidthDefault(value, context, m.Column);

                    if (m.Column == null || !m.Column.IsIdentity)
                    {
                        builder.Append(seg);
                        length = builder.Length;
                        builder.Append(',');
                        builder.AppendNewLine();
                    }

                    if (m.Column != null && m.Column.IsKey)
                    {
                        useKey = true;
                        whereBuilder.AppendMember("t0", m.Member, typeRuntime.Type);
                        whereBuilder.Append(" = ");
                        whereBuilder.Append(seg);
                        whereBuilder.Append(" AND ");
                    }
                }

                if (!useKey)
                {
                    throw new XFrameworkException("Update<T>(T value) require T must have key column.");
                }

                builder.Length       = length;
                whereBuilder.Length -= 5;

                builder.AppendNewLine();
                builder.Append("WHERE ");
                builder.Append(whereBuilder);
            }
            else if (tree.Expression != null)
            {
                AliasGenerator      ag      = this.PrepareTableAlias(tree.Select, context.AliasPrefix);
                DbExpressionVisitor visitor = null;
                var cmd = new DbSelectCommand(context, ag, tree.Select.SelectHasMany);

                if (tree.Select.Joins != null)
                {
                    visitor = new JoinExpressionVisitor(ag, cmd.JoinFragment);
                    visitor.Visit(tree.Select.Joins);
                }

                cmd.WhereFragment.AppendNewLine();
                cmd.WhereFragment.AppendNewLine("SET");
                visitor = new UpdateExpressionVisitor(ag, cmd.WhereFragment);
                visitor.Visit(tree.Expression);

                if (tree.Select.Wheres != null)
                {
                    visitor = new WhereExpressionVisitor(ag, cmd.WhereFragment);
                    visitor.Visit(tree.Select.Wheres);
                    cmd.AddNavMembers(visitor.NavMembers);
                }

                builder.Append(cmd.CommandText);
            }

            builder.Append(';');
            return(new DbRawCommand(builder.ToString(), builder.TranslateContext != null ? builder.TranslateContext.Parameters : null, System.Data.CommandType.Text));
        }
Ejemplo n.º 13
0
        // 创建 UPDATE 命令
        protected override CommandDefine ParseUpdateCommand <T>(DbQueryableInfo_Update <T> qUpdate)
        {
            SqlBuilder builder = new SqlBuilder(this.EscCharLeft, this.EscCharRight);
            var        rInfo   = TypeRuntimeInfoCache.GetRuntimeInfo <T>();

            builder.Append("UPDATE t0 SET");
            builder.AppendNewLine();

            if (qUpdate.Entity != null)
            {
                object     entity       = qUpdate.Entity;
                SqlBuilder whereBuilder = new SqlBuilder(this.EscCharLeft, this.EscCharRight);
                bool       useKey       = false;
                int        length       = 0;

                foreach (var kv in rInfo.Wrappers)
                {
                    var wrapper = kv.Value as MemberAccessWrapper;
                    var column  = wrapper.Column;
                    if (column != null && column.IsIdentity)
                    {
                        continue;
                    }
                    if (column != null && column.NoMapped)
                    {
                        continue;
                    }
                    if (wrapper.ForeignKey != null)
                    {
                        continue;
                    }

                    builder.AppendMember("t0", wrapper.Member.Name);
                    builder.Append(" = ");

                    var value = wrapper.Get(entity);
                    var seg   = ExpressionVisitorBase.GetSqlValue(value);
                    builder.Append(seg);
                    length = builder.Length;
                    builder.Append(',');
                    builder.AppendNewLine();

                    if (column != null && column.IsKey)
                    {
                        useKey = true;
                        whereBuilder.AppendMember("t0", wrapper.Member.Name);
                        whereBuilder.Append(" = ");
                        whereBuilder.Append(seg);
                        whereBuilder.Append(" AND ");
                    }
                }

                if (!useKey)
                {
                    throw new XfwException("Update<T>(T value) require T must have key column.");
                }

                builder.Length       = length;
                whereBuilder.Length -= 5;

                builder.AppendNewLine();
                builder.Append("FROM ");
                builder.AppendMember(rInfo.TableName);
                builder.Append(" t0");


                builder.AppendNewLine();
                builder.Append("WHERE ");
                builder.Append(whereBuilder);
            }
            else if (qUpdate.Expression != null)
            {
                TableAliasCache       aliases = this.PrepareAlias <T>(qUpdate.SelectInfo);
                ExpressionVisitorBase visitor = null;
                visitor = new UpdateExpressionVisitor(this, aliases, qUpdate.Expression);
                visitor.Write(builder);

                builder.AppendNewLine();
                builder.Append("FROM ");
                builder.AppendMember(rInfo.TableName);
                builder.AppendAs("t0");

                var sc = new CommandDefine_Select.Builder(this.EscCharLeft, this.EscCharRight, aliases);

                visitor = new JoinExpressionVisitor(this, aliases, qUpdate.SelectInfo.Join);
                visitor.Write(sc.JoinFragment);

                visitor = new WhereExpressionVisitor(this, aliases, qUpdate.SelectInfo.Where);
                visitor.Write(sc.WhereFragment);
                sc.AddNavigation(visitor.Navigations);

                builder.Append(sc.Command);
            }

            return(new CommandDefine(builder.ToString(), null, System.Data.CommandType.Text)); //builder.ToString();
        }
Ejemplo n.º 14
0
        /// <summary>
        /// 创建 UPDATE 命令
        /// </summary>
        /// <param name="tree">查询语义</param>
        /// <param name="context">解析SQL命令上下文</param>
        /// <returns></returns>
        protected override DbRawCommand TranslateUpdateCommand <T>(DbQueryUpdateTree tree, ITranslateContext context)
        {
            ISqlBuilder builder     = this.CreateSqlBuilder(context);
            var         typeRuntime = TypeRuntimeInfoCache.GetRuntimeInfo <T>();

            builder.Append("UPDATE ");
            builder.AppendTable(typeRuntime.TableSchema, typeRuntime.TableName, typeRuntime.IsTemporary);
            builder.Append(" t0 SET");
            builder.AppendNewLine();

            if (tree.Entity != null)
            {
                object      entity       = tree.Entity;
                ISqlBuilder whereBuilder = this.CreateSqlBuilder(context);
                bool        useKey       = false;
                int         length       = 0;

                foreach (var item in typeRuntime.Members)
                {
                    var m = item as FieldAccessorBase;
                    if (m == null || !m.IsDbField)
                    {
                        continue;
                    }

                    if (m.Column != null && m.Column.IsIdentity)
                    {
                        goto LABEL;                                          // Fix issue# 自增列同时又是主键
                    }
                    builder.AppendMember(null, m.Member, typeRuntime.Type);
                    builder.Append(" = ");

LABEL:
                    if (m.Column == null || !m.Column.IsIdentity)
                    {
                        var value         = m.Invoke(entity);
                        var sqlExpression = this.Constor.GetSqlValueWidthDefault(value, context, m.Column);

                        builder.Append(sqlExpression);
                        length = builder.Length;
                        builder.Append(',');
                        builder.AppendNewLine();
                    }

                    if (m.Column != null && m.Column.IsKey)
                    {
                        useKey = true;
                    }
                }

                if (!useKey)
                {
                    throw new XFrameworkException("Update<T>(T value) require T must have key column.");
                }


                // ORACLE 需要注意参数顺序问题
                int index = -1;
                foreach (FieldAccessorBase m in typeRuntime.KeyMembers)
                {
                    var column = m.Column;
                    var value  = m.Invoke(entity);
                    var seg    = this.Constor.GetSqlValueWidthDefault(value, context, column);
                    index += 1;

                    whereBuilder.AppendMember(null, m.Member, typeRuntime.Type);
                    whereBuilder.Append(" = ");
                    whereBuilder.Append(seg);
                    if (index < typeRuntime.KeyMembers.Count - 1)
                    {
                        whereBuilder.Append(" AND ");
                    }
                }

                builder.Length = length;
                builder.AppendNewLine();
                builder.Append("WHERE ");
                builder.Append(whereBuilder);
            }
            else if (tree.Expression != null)
            {
                // SELECT 表达式
                LambdaExpression lambda = tree.Expression as LambdaExpression;
                var        body         = lambda.Body;
                Expression expression   = null;
                if (body.NodeType == ExpressionType.MemberInit)
                {
                    var initExpression = body as MemberInitExpression;
                    var bindings       = new List <MemberBinding>(initExpression.Bindings);
                    foreach (var m in typeRuntime.KeyMembers)
                    {
                        var member  = Expression.MakeMemberAccess(lambda.Parameters[0], m.Member);
                        var binding = Expression.Bind(m.Member, member);
                        if (!bindings.Any(x => x.Member == m.Member))
                        {
                            bindings.Add(binding);
                        }
                    }
                    expression = Expression.MemberInit(initExpression.NewExpression, bindings);
                }
                else if (body.NodeType == ExpressionType.New)
                {
                    var newExpression = body as NewExpression;
                    var bindings      = new List <MemberBinding>();
                    for (int index = 0; index < newExpression.Members.Count; index++)
                    {
                        var m = (FieldAccessorBase)typeRuntime.Members.FirstOrDefault(a => a is FieldAccessorBase && (
                                                                                          a.Name == newExpression.Members[index].Name ||
                                                                                          ((FieldAccessorBase)a).Column != null && ((FieldAccessorBase)a).Column.Name == newExpression.Members[index].Name));
                        if (m == null)
                        {
                            throw new XFrameworkException("Member {0}.{1} not found.", typeRuntime.Type.Name, newExpression.Members[index].Name);
                        }
                        var binding = Expression.Bind(m.Member, newExpression.Arguments[index].Type != m.CLRType
                            ? Expression.Convert(newExpression.Arguments[index], m.CLRType)
                            : newExpression.Arguments[index]);
                        bindings.Add(binding);
                    }

                    foreach (var m in typeRuntime.KeyMembers)
                    {
                        var member  = Expression.MakeMemberAccess(lambda.Parameters[0], m.Member);
                        var binding = Expression.Bind(m.Member, member);
                        if (!bindings.Any(x => x.Member == m.Member))
                        {
                            bindings.Add(binding);
                        }
                    }

                    var newExpression2 = Expression.New(typeRuntime.Constructor.Member);
                    expression = Expression.MemberInit(newExpression2, bindings);
                }

                // 解析查询以确定是否需要嵌套
                tree.Select.Select = new DbExpression(DbExpressionType.Select, expression);
                var cmd = (DbSelectCommand)this.TranslateSelectCommand(tree.Select, 0, false, this.CreateTranslateContext(context.DbContext));

                if ((cmd.NavMembers != null && cmd.NavMembers.Count > 0) || (tree.Select.Joins != null && tree.Select.Joins.Count > 0))
                {
                    // 无法使用 DISTINCT, GROUP BY 等子句从视图中选择 ROWID 或采样。UPDATE 不能用rowid
                    // 有导航属性或者关联查询,使用 MERGE INTO 语法。要求必须有主键

                    if (typeRuntime.KeyMembers == null || typeRuntime.KeyMembers.Count == 0)
                    {
                        throw new XFrameworkException("Update<T>(Expression<Func<T, object>> updateExpression) require entity must have key column.");
                    }

                    builder.Length = 0;
                    builder.Append("MERGE INTO ");
                    builder.AppendTable(typeRuntime.TableSchema, typeRuntime.TableName, typeRuntime.IsTemporary);
                    builder.AppendNewLine(" t0");
                    builder.Append("USING (");

                    cmd = (DbSelectCommand)this.TranslateSelectCommand(tree.Select, 1, false, context);
                    builder.AppendNewLine(cmd.CommandText);
                    builder.Append(") t1 ON (");
                    foreach (var m in typeRuntime.KeyMembers)
                    {
                        builder.AppendMember("t0", m.Member, typeRuntime.Type);
                        builder.Append(" = ");
                        builder.AppendMember("t1", m.Member, typeRuntime.Type);
                        builder.Append(" AND ");
                    }
                    builder.Length -= 5;
                    builder.Append(')');

                    // UPDATE
                    builder.AppendNewLine();
                    builder.AppendNewLine("WHEN MATCHED THEN UPDATE SET ");

                    // SET 字段
                    var visitor = new OracleUpdateExpressionVisitor(null, builder);
                    visitor.Visit(tree.Expression);
                }
                else
                {
                    // 直接 SQL 的 UPDATE 语法
                    DbExpressionVisitor visitor = null;
                    AliasGenerator      ag      = this.PrepareTableAlias(tree.Select, context.AliasPrefix);
                    visitor = new UpdateExpressionVisitor(ag, builder);
                    visitor.Visit(tree.Expression);

                    if (tree.Select.Wheres != null)
                    {
                        visitor = new WhereExpressionVisitor(ag, builder);
                        visitor.Visit(tree.Select.Wheres);
                    }
                }
            }

            builder.Append(';');
            return(new DbRawCommand(builder.ToString(), builder.TranslateContext != null ? builder.TranslateContext.Parameters : null, System.Data.CommandType.Text));
        }