Beispiel #1
0
        /// <inheritdoc />
        protected override void VisitCore(MethodCallExpression node)
        {
            string name = node.Method.Name;

            if (isNoPackage)
            {
                switch (name)
                {
                case MethodCall.TimeOut:
                case MethodCall.From:
                case MethodCall.Any:
                case MethodCall.All:
                case MethodCall.Contains:
                case MethodCall.Union:
                case MethodCall.Concat:
                case MethodCall.Except:
                case MethodCall.Intersect:
                    break;

                default:
                    isNoPackage = false;
                    break;
                }
            }


            switch (name)
            {
            case MethodCall.ElementAt:
            case MethodCall.ElementAtOrDefault:

                base.Visit(node.Arguments[0]);

                int index = (int)node.Arguments[1].GetValueFromExpression();

                if (index < 0)
                {
                    throw new IndexOutOfRangeException();
                }

                if (this.take > 0 && index < this.take)
                {
                    throw new IndexOutOfRangeException();
                }

                this.take = 1;

                this.skip += index;

                break;

            case MethodCall.Take:
            case MethodCall.TakeLast:

                if (useAggregation)
                {
                    throw new DSyntaxErrorException($"使用聚合函数时,禁止使用分页函数({name})!");
                }

                if (name == MethodCall.TakeLast)
                {
                    reverseOrder ^= true;
                }

                int take = (int)node.Arguments[1].GetValueFromExpression();

                if (take < 1)
                {
                    throw new ArgumentOutOfRangeException($"使用{name}函数,参数值必须大于零!");
                }

                if (this.take > 0 && take < this.take)
                {
                    throw new IndexOutOfRangeException();
                }

                if (this.skip > -1)
                {
                    if (this.skip > take)
                    {
                        throw new IndexOutOfRangeException();
                    }

                    take -= this.skip;
                }

                if (this.take == -1)
                {
                    this.take = take;
                }

                base.Visit(node.Arguments[0]);

                if (!useOrderBy && name == MethodCall.TakeLast)
                {
                    throw new DSyntaxErrorException($"使用函数({name})时,必须使用排序函数(OrderBy/OrderByDescending)!");
                }

                break;

            case MethodCall.First:
            case MethodCall.FirstOrDefault:
            case MethodCall.Single:
            case MethodCall.SingleOrDefault:

                // TOP(1)
                this.take = 1;

                if (node.Arguments.Count > 1)
                {
                    VisitCondition(node);
                }
                else
                {
                    Visit(node.Arguments[0]);
                }

                break;

            case MethodCall.Last:
            case MethodCall.LastOrDefault:

                // TOP(..)
                this.take = 1;

                reverseOrder ^= true;

                if (node.Arguments.Count > 1)
                {
                    VisitCondition(node);
                }
                else
                {
                    base.Visit(node.Arguments[0]);
                }

                if (!useOrderBy)
                {
                    throw new DSyntaxErrorException($"使用函数({name})时,必须使用排序函数(OrderBy/OrderByDescending)!");
                }

                break;

            case MethodCall.Max:
                buildSelect    = false;
                buildedSelect  = true;
                useAggregation = true;
                using (var visitor = new MaxVisitor(this))
                {
                    visitor.Startup(node);
                }
                break;

            case MethodCall.Min:
                buildSelect    = false;
                buildedSelect  = true;
                useAggregation = true;
                using (var visitor = new MinVisitor(this))
                {
                    visitor.Startup(node);
                }
                break;

            case MethodCall.Sum:
                buildSelect    = false;
                buildedSelect  = true;
                useAggregation = true;
                using (var visitor = new SumVisitor(this))
                {
                    visitor.Startup(node);
                }
                break;

            case MethodCall.Average:
                buildSelect    = false;
                buildedSelect  = true;
                useAggregation = true;
                using (var visitor = new AverageVisitor(this))
                {
                    visitor.Startup(node);
                }
                break;

            case MethodCall.Count:
            case MethodCall.LongCount:
                useCount       = true;
                buildSelect    = false;
                buildedSelect  = true;
                useAggregation = true;
                using (var visitor = new CountVisitor(this))
                {
                    visitor.Startup(node);
                }
                break;

            case MethodCall.Skip:
            case MethodCall.SkipLast:

                if (useAggregation)
                {
                    throw new DSyntaxErrorException($"使用聚合函数时,禁止使用分页函数({name})!");
                }

                if (name == MethodCall.SkipLast)
                {
                    reverseOrder ^= true;
                }

                int skip = (int)node.Arguments[1].GetValueFromExpression();

                if (skip < 0)
                {
                    throw new ArgumentOutOfRangeException($"使用({name})函数,参数值不能小于零!");
                }

                if (this.skip == -1)
                {
                    this.skip = skip;
                }
                else
                {
                    this.skip += skip;
                }

                base.Visit(node.Arguments[0]);

                if (!useOrderBy && name == MethodCall.SkipLast)
                {
                    throw new DSyntaxErrorException($"使用函数({name})时,必须使用排序函数(OrderBy/OrderByDescending)!");
                }

                break;

            case MethodCall.Reverse:

                reverseOrder ^= true;

                base.Visit(node.Arguments[0]);

                if (!useOrderBy)
                {
                    throw new DSyntaxErrorException($"使用函数“{name}”时,必须使用排序函数(OrderBy/OrderByDescending)!");
                }

                break;

            case MethodCall.GroupBy:
            case MethodCall.Where when node.Arguments[0].Type.IsGroupingQueryable():
            case MethodCall.TakeWhile when node.Arguments[0].Type.IsGroupingQueryable():
            case MethodCall.SkipWhile when node.Arguments[0].Type.IsGroupingQueryable():

                if (name == MethodCall.GroupBy)
                {
                    castCache.Add(node.Type.GetGenericArguments().First(), TypeToEntryType(node.Arguments[0].Type));
                }

                if (!useGroupBy)
                {
                    byVisitor = new GroupByVisitor(this, defaultCache, groupByExpressions);
                }

                useGroupBy = true;

                byVisitor.Startup(node);

                break;

            case MethodCall.Select:

                if (useCount)
                {
                    base.Visit(node.Arguments[0]);

                    break;
                }

                if (!buildSelect)
                {
                    throw new DSyntaxErrorException($"请将函数“{name}”置于查询最后一个包含入参的函数之后!");
                }

                buildSelect = buildFrom = false;

                writer.Select();

                Workflow(() =>
                {
                    buildedSelect = true;

                    if (isDistinct)
                    {
                        writer.Distinct();
                    }

                    inSelect = true;

                    Visit(node.Arguments[1]);

                    inSelect = false;

                    writer.From();

                    if (!hasJoin && !hasCombination)
                    {
                        WriteTableName(node.Arguments[0].Type);
                    }
                }, () => base.Visit(node.Arguments[0]));

                break;

            case MethodCall.SelectMany when node.Arguments.Count == 3:

                hasJoin = true;

                var parameterExp = Join(node.Arguments[2], true);

                bool DoneLeftJoin(ParameterExpression parameter, Expression expression)
                {
                    if (expression.NodeType == ExpressionType.MemberAccess)
                    {
                        return(false);
                    }

                    switch (expression)
                    {
                    case UnaryExpression unary:
                        return(DoneLeftJoin(parameter, unary.Operand));

                    case LambdaExpression lambda when lambda.Parameters.Count == 1:
                        return(DoneLeftJoin(parameter, lambda.Body));

                    case MethodCallExpression methodCall when methodCall.Method.Name == MethodCall.DefaultIfEmpty:

                        if (methodCall.Arguments.Count > 1)
                        {
                            defaultCache.Add(Tuple.Create(parameter.Type, parameter.Name), methodCall.Arguments[1]);
                        }

                        return(true);

                    default:
                        throw new DSyntaxErrorException();
                    }
                }

                using (var visitor = new GroupJoinVisitor(this, parameterExp, DoneLeftJoin(parameterExp, node.Arguments[1])))
                {
                    visitor.Startup(node.Arguments[0]);
                }

                buildTable = false;

                break;

            case MethodCall.Distinct:

                if (buildedSelect)
                {
                    throw new DSyntaxErrorException($"函数“{name}”未生效!");
                }

                isDistinct = true;

                base.Visit(node.Arguments[0]);

                break;

            case MethodCall.Cast:
            case MethodCall.OfType:
                Type type = node.Type
                            .GetGenericArguments()
                            .First();

                if (type.IsValueType || type == typeof(string) || typeof(IEnumerable).IsAssignableFrom(type))
                {
                    throw new TypeAccessInvalidException($"“{node.Method.Name}”函数泛型参数类型不能是值类型、字符串类型或迭代类型!");
                }

                var objExp = node.Arguments[0];

                var originalType = objExp.Type;

                if (node.Type == originalType)
                {
                    base.Visit(objExp);

                    break;
                }

                useCast = true;

                if (!castCache.ContainsKey(type))
                {
                    castCache.Add(type, TypeToEntryType(originalType));
                }

                var entry = TypeItem.Get(type);

                if (memberFilters.Count == 0)
                {
                    memberFilters.AddRange(entry.PropertyStores
                                           .Where(x => x.CanRead && x.CanWrite)
                                           .Select(x => x.Name.ToLower()));
                }
                else     //? 取交集
                {
                    memberFilters = memberFilters
                                    .Intersect(entry.PropertyStores
                                               .Where(x => x.CanRead && x.CanWrite)
                                               .Select(x => x.Name.ToLower()))
                                    .ToList();
                }

                if (memberFilters.Count == 0)
                {
                    throw new DException("未指定查询字段!");
                }

                base.Visit(objExp);

                break;

            case MethodCall.OrderBy:
            case MethodCall.ThenBy:
            case MethodCall.OrderByDescending:
            case MethodCall.ThenByDescending:

                useOrderBy = true;

                if (useAggregation)
                {
                    base.Visit(node.Arguments[0]);

                    break;
                }

                bool thatReverseOrder = reverseOrder;

                Workflow(() => writer.UsingSort(() =>
                {
                    orderBySwitch.OrderBy();

                    base.Visit(node.Arguments[1]);

                    if (thatReverseOrder ^ node.Method.Name.EndsWith("Descending"))
                    {
                        writer.Descending();
                    }
                }), () => base.Visit(node.Arguments[0]));

                break;

            case MethodCall.Join:

                hasJoin = true;

                Join(node.Arguments[2], false);

                Join(node.Arguments[3], true);

                using (var visitor = new JoinVisitor(this))
                {
                    visitor.Startup(node);
                }

                buildTable = false;
                break;

            case MethodCall.Any:
                isExists = true;
                using (var visitor = new AnyVisitor(this))
                {
                    visitor.Startup(node);
                }
                break;

            case MethodCall.All:
                isExists = true;
                using (var visitor = new AllVisitor(this))
                {
                    visitor.Startup(node);
                }
                break;

            case MethodCall.Union:
            case MethodCall.Concat:
            case MethodCall.Except:
            case MethodCall.Intersect:

                buildTable     = buildFrom = false;
                hasCombination = true;

                if (isNoPackage)
                {
                    buildSelect   = false;
                    buildedSelect = true;
                    using (var visitor = new CombinationVisitor(this))
                    {
                        visitor.Startup(node);
                    }

                    break;
                }

                string prefix = "x";

                if (buildSelect)
                {
                    buildSelect = false;

                    var tableInfo = MakeTableInfo(node.Arguments[0].Type);

                    Workflow(() =>
                    {
                        buildedSelect = true;

                        writer.Select();

                        if (isDistinct)
                        {
                            writer.Distinct();
                        }

                        WriteMembers(prefix, FilterMembers(tableInfo.ReadOrWrites));
                    }, Done);

                    break;
                }

                Done();

                break;

                void Done()
                {
                    writer.From();

                    writer.OpenBrace();

                    using (var visitor = new CombinationVisitor(this))
                    {
                        visitor.Startup(node);
                    }

                    writer.CloseBrace();

                    writer.WhiteSpace();

                    writer.Name(prefix = GetEntryAlias(node.Arguments[0].Type, "x"));
                }

            default:
                base.VisitCore(node);

                break;
            }

            ParameterExpression Join(Expression expression, bool isJoin)
            {
                switch (expression)
                {
                case UnaryExpression unary:
                    return(Join(unary.Operand, isJoin));

                case LambdaExpression lambda when lambda.Parameters.Count == 1 || lambda.Parameters.Count == 2:
#if NETSTANDARD2_1_OR_GREATER
                    var parameter = lambda.Parameters[^ 1];
Beispiel #2
0
 /// <inheritdoc />
 public HavingVisitor(GroupByVisitor visitor, Dictionary <MemberInfo, Expression> groupByExpressions) : base(visitor)
 {
     this.visitor            = visitor;
     this.groupByExpressions = groupByExpressions;
 }