Пример #1
0
 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
 {
     return(methodCall.IsSameGenericMethod(Methods.LinqToDB.DisableGuard));
 }
Пример #2
0
        internal static void ParseSet(
            ExpressionBuilder builder,
            BuildInfo buildInfo,
            LambdaExpression extract,
            LambdaExpression update,
            IBuildContext select,
            SqlTable table,
            List <SelectQuery.SetExpression> items)
        {
            var ext = extract.Body;

            while (ext.NodeType == ExpressionType.Convert || ext.NodeType == ExpressionType.ConvertChecked)
            {
                ext = ((UnaryExpression)ext).Operand;
            }

            if (ext.NodeType != ExpressionType.MemberAccess || ext.GetRootObject() != extract.Parameters[0])
            {
                throw new LinqException("Member expression expected for the 'Set' statement.");
            }

            var body   = (MemberExpression)ext;
            var member = body.Member;

            if (member is MethodInfo)
            {
                member = ((MethodInfo)member).GetPropertyInfo();
            }

            var members = body.GetMembers();
            var name    = members
                          .Skip(1)
                          .Select(ex =>
            {
                var me = ex as MemberExpression;

                if (me == null)
                {
                    return(null);
                }

                var m = me.Member;

                if (m is MethodInfo)
                {
                    m = ((MethodInfo)m).GetPropertyInfo();
                }

                return(m);
            })
                          .Where(m => m != null && !m.IsNullableValueMember())
                          .Select(m => m.Name)
                          .Aggregate((s1, s2) => s1 + "." + s2);

            if (table != null && !table.Fields.ContainsKey(name))
            {
                throw new LinqException("Member '{0}.{1}' is not a table column.", member.DeclaringType.Name, name);
            }

            var column = table != null ?
                         table.Fields[name] :
                         select.ConvertToSql(
                body, 1, ConvertFlags.Field)[0].Sql;
            //Expression.MakeMemberAccess(Expression.Parameter(member.DeclaringType, "p"), member), 1, ConvertFlags.Field)[0].Sql;
            var sp   = select.Parent;
            var ctx  = new ExpressionContext(buildInfo.Parent, select, update);
            var expr = builder.ConvertToSqlExpression(ctx, update.Body);

            builder.ReplaceParent(ctx, sp);

            items.Add(new SelectQuery.SetExpression(column, expr));
        }
Пример #3
0
 protected override SequenceConvertInfo Convert(
     ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo, ParameterExpression param)
 {
     return(null);
 }
Пример #4
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            UpdateBuilder.BuildSetter(
                builder,
                buildInfo,
                (LambdaExpression)methodCall.Arguments[1].Unwrap(),
                sequence,
                sequence.SqlQuery.Insert.Items,
                sequence);

            UpdateBuilder.BuildSetter(
                builder,
                buildInfo,
                (LambdaExpression)methodCall.Arguments[2].Unwrap(),
                sequence,
                sequence.SqlQuery.Update.Items,
                sequence);

            sequence.SqlQuery.Insert.Into  = ((TableBuilder.TableContext)sequence).SqlTable;
            sequence.SqlQuery.Update.Table = ((TableBuilder.TableContext)sequence).SqlTable;
            sequence.SqlQuery.From.Tables.Clear();
            sequence.SqlQuery.From.Table(sequence.SqlQuery.Update.Table);

            if (methodCall.Arguments.Count == 3)
            {
                var table = sequence.SqlQuery.Insert.Into;
                var keys  = table.GetKeys(false);

                if (keys.Count == 0)
                {
                    throw new LinqException("InsertOrUpdate method requires the '{0}' table to have a primary key.", table.Name);
                }

                var q =
                    (
                        from k in keys
                        join i in sequence.SqlQuery.Insert.Items on k equals i.Column
                        select new { k, i }
                    ).ToList();

                var missedKey = keys.Except(q.Select(i => i.k)).FirstOrDefault();

                if (missedKey != null)
                {
                    throw new LinqException("InsertOrUpdate method requires the '{0}.{1}' field to be included in the insert setter.",
                                            table.Name,
                                            ((SqlField)missedKey).Name);
                }

                sequence.SqlQuery.Update.Keys.AddRange(q.Select(i => i.i));
            }
            else
            {
                UpdateBuilder.BuildSetter(
                    builder,
                    buildInfo,
                    (LambdaExpression)methodCall.Arguments[3].Unwrap(),
                    sequence,
                    sequence.SqlQuery.Update.Keys,
                    sequence);
            }

            sequence.SqlQuery.QueryType = QueryType.InsertOrUpdate;

            return(new InsertOrUpdateContext(buildInfo.Parent, sequence));
        }
Пример #5
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            var selector = (LambdaExpression)methodCall.Arguments[1].Unwrap();

            // reset LoadWith sequence
            if (methodCall.IsQueryable("LoadWith"))
            {
                for (;;)
                {
                    if (sequence is LoadWithContext lw)
                    {
                        sequence = lw.Context;
                    }
                    else
                    {
                        break;
                    }
                }
            }

            var path  = selector.Body.Unwrap();
            var table = GetTableContext(sequence, path, out var level);

            var associations = ExtractAssociations(builder, path, level)
                               .Reverse()
                               .ToArray();

            if (associations.Length == 0)
            {
                throw new LinqToDBException($"Unable to retrieve properties path for LoadWith/ThenLoad. Path: '{path}'");
            }

            if (methodCall.Method.Name == "ThenLoad")
            {
                if (!(table.LoadWith?.Count > 0))
                {
                    throw new LinqToDBException($"ThenLoad function should be followed after LoadWith. Can not find previous property for '{path}'.");
                }

                var lastPath = table.LoadWith[table.LoadWith.Count - 1];
                associations = Array <LoadWithInfo> .Append(lastPath, associations);

                if (methodCall.Arguments.Count == 3)
                {
                    var lastElement = associations[associations.Length - 1];
                    lastElement.FilterFunc = (Expression?)methodCall.Arguments[2];
                    CheckFilterFunc(lastElement.MemberInfo.GetMemberType(), lastElement.FilterFunc !.Type, builder.MappingSchema);
                }

                // append to the last member chain
                table.LoadWith[table.LoadWith.Count - 1] = associations;
            }
            else
            {
                if (table.LoadWith == null)
                {
                    table.LoadWith = new List <LoadWithInfo[]>();
                }

                if (methodCall.Arguments.Count == 3)
                {
                    var lastElement = associations[associations.Length - 1];
                    lastElement.FilterFunc = (Expression?)methodCall.Arguments[2];
                    CheckFilterFunc(lastElement.MemberInfo.GetMemberType(), lastElement.FilterFunc !.Type, builder.MappingSchema);
                }

                table.LoadWith.Add(associations);
            }

            var loadWithSequence = sequence as LoadWithContext ?? new LoadWithContext(sequence, table);

            return(loadWithSequence);
        }
Пример #6
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence1 = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));
            var sequence2 = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()));

            SetOperation setOperation;

            switch (methodCall.Method.Name)
            {
            case "Concat":
            case "UnionAll": setOperation = SetOperation.UnionAll;     break;

            case "Union": setOperation = SetOperation.Union;        break;

            case "Except": setOperation = SetOperation.Except;       break;

            case "ExceptAll": setOperation = SetOperation.ExceptAll;    break;

            case "Intersect": setOperation = SetOperation.Intersect;    break;

            case "IntersectAll": setOperation = SetOperation.IntersectAll; break;

            default:
                throw new ArgumentException($"Invalid method name {methodCall.Method.Name}.");
            }

            var needsEmulation = !builder.DataContext.SqlProviderFlags.IsAllSetOperationsSupported &&
                                 (setOperation == SetOperation.ExceptAll || setOperation == SetOperation.IntersectAll)
                                 ||
                                 !builder.DataContext.SqlProviderFlags.IsDistinctSetOperationsSupported &&
                                 (setOperation == SetOperation.Except || setOperation == SetOperation.Intersect);

            if (needsEmulation)
            {
                // emulation

                var sequence = new SubQueryContext(sequence1);
                var query    = sequence2;
                var except   = query.SelectQuery;

                var sql = sequence.SelectQuery;

                if (setOperation == SetOperation.Except || setOperation == SetOperation.Intersect)
                {
                    sql.Select.IsDistinct = true;
                }

                except.ParentSelect = sql;

                if (setOperation == SetOperation.Except || setOperation == SetOperation.ExceptAll)
                {
                    sql.Where.Not.Exists(except);
                }
                else
                {
                    sql.Where.Exists(except);
                }

                var keys1 = sequence.ConvertToSql(null, 0, ConvertFlags.All);
                var keys2 = query.ConvertToSql(null, 0, ConvertFlags.All);

                if (keys1.Length != keys2.Length)
                {
                    throw new InvalidOperationException();
                }

                for (var i = 0; i < keys1.Length; i++)
                {
                    except.Where
                    .Expr(keys1[i].Sql)
                    .Equal
                    .Expr(keys2[i].Sql);
                }

                return(sequence);
            }

            var set1 = new SubQueryContext(sequence1);
            var set2 = new SubQueryContext(sequence2);

            var setOperator = new SqlSetOperator(set2.SelectQuery, setOperation);

            set1.SelectQuery.SetOperators.Add(setOperator);

            return(new SetOperationContext(set1, set2, methodCall));
        }
Пример #7
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequenceExpr             = methodCall.Arguments[0];
            var wrapSequence             = false;
            LambdaExpression?groupingKey = null;
            var groupingKind             = GroupingType.Default;

            if (sequenceExpr.NodeType == ExpressionType.Call)
            {
                var call = (MethodCallExpression)methodCall.Arguments[0];

                if (call.IsQueryable("Select"))
                {
                    var selectParam = (LambdaExpression)call.Arguments[1].Unwrap();
                    var type        = selectParam.Body.Type;

                    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ExpressionBuilder.GroupSubQuery <,>))
                    {
                        wrapSequence = true;

                        var selectParamBody = selectParam.Body.Unwrap();
                        MethodCallExpression?groupingMethod = null;
                        if (selectParamBody is MemberInitExpression mi)
                        {
                            var assignment = mi.Bindings.OfType <MemberAssignment>().FirstOrDefault(m => m.Member.Name == "Key");
                            if (assignment?.Expression.NodeType == ExpressionType.Call)
                            {
                                var mc = (MethodCallExpression)assignment.Expression;
                                if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.Rollup, Methods.LinqToDB.GroupBy.Cube, Methods.LinqToDB.GroupBy.GroupingSets))
                                {
                                    groupingMethod = mc;
                                    groupingKey    = (LambdaExpression)mc.Arguments[0].Unwrap();
                                    if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.Rollup))
                                    {
                                        groupingKind = GroupingType.Rollup;
                                    }
                                    else if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.Cube))
                                    {
                                        groupingKind = GroupingType.Cube;
                                    }
                                    else if (mc.IsSameGenericMethod(Methods.LinqToDB.GroupBy.GroupingSets))
                                    {
                                        groupingKind = GroupingType.GroupBySets;
                                    }
                                    else
                                    {
                                        throw new InvalidOperationException();
                                    }
                                }
                            }
                        }

                        if (groupingMethod != null && groupingKey != null)
                        {
                            sequenceExpr = sequenceExpr.Replace(groupingMethod, groupingKey.Body.Unwrap());
                        }
                    }
                }
            }

            var sequence    = builder.BuildSequence(new BuildInfo(buildInfo, sequenceExpr));
            var keySequence = sequence;

            var groupingType    = methodCall.Type.GetGenericArguments()[0];
            var keySelector     = (LambdaExpression)methodCall.Arguments[1].Unwrap() !;
            var elementSelector = (LambdaExpression)methodCall.Arguments[2].Unwrap() !;

            if (wrapSequence)
            {
                sequence = new SubQueryContext(sequence);
            }

            sequence = new SubQueryContext(sequence);
            var key = new KeyContext(buildInfo.Parent, keySelector, sequence);

            if (groupingKind != GroupingType.GroupBySets)
            {
                var groupSql = builder.ConvertExpressions(key, keySelector.Body.Unwrap(), ConvertFlags.Key, null);

                var allowed = groupSql.Where(s => !QueryHelper.IsConstant(s.Sql));

                foreach (var sql in allowed)
                {
                    sequence.SelectQuery.GroupBy.Expr(sql.Sql);
                }
            }
            else
            {
                var goupingSetBody = groupingKey !.Body;
                var groupingSets   = EnumGroupingSets(goupingSetBody).ToArray();
                if (groupingSets.Length == 0)
                {
                    throw new LinqException($"Invalid groping sets expression '{goupingSetBody}'.");
                }

                foreach (var groupingSet in groupingSets)
                {
                    var groupSql = builder.ConvertExpressions(keySequence, groupingSet, ConvertFlags.Key, null);
                    sequence.SelectQuery.GroupBy.Items.Add(
                        new SqlGroupingSet(groupSql.Select(s => keySequence.SelectQuery.Select.AddColumn(s.Sql))));
                }
            }

            sequence.SelectQuery.GroupBy.GroupingType = groupingKind;

            var element = new SelectContext(buildInfo.Parent, elementSelector, sequence /*, key*/);
            var groupBy = new GroupByContext(buildInfo.Parent, sequenceExpr, groupingType, sequence, key, element, builder.IsGroupingGuardDisabled);

            Debug.WriteLine("BuildMethodCall GroupBy:\n" + groupBy.SelectQuery);

            return(groupBy);
        }
Пример #8
0
 public bool IsSequence(ExpressionBuilder builder, BuildInfo buildInfo)
 {
     return(builder.IsSequence(new BuildInfo(buildInfo, ((MethodCallExpression)buildInfo.Expression).Arguments[0])));
 }
Пример #9
0
 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
 {
     return(methodCall.Arguments.Count == 2 && methodCall.IsQueryable("Concat", "UnionAll", "Union", "Except", "Intersect", "ExceptAll", "IntersectAll"));
 }
Пример #10
0
        public IBuildContext BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo)
        {
            var call = (MethodCallExpression)buildInfo.Expression;

            return(new Context(builder.BuildSequence(new BuildInfo(buildInfo, call.Arguments[0]))));
        }
Пример #11
0
 public SequenceConvertInfo?Convert(ExpressionBuilder builder, BuildInfo buildInfo, ParameterExpression?param)
 {
     return(null);
 }
Пример #12
0
        public bool CanBuild(ExpressionBuilder builder, BuildInfo buildInfo)
        {
            var call = buildInfo.Expression as MethodCallExpression;

            return(call != null && call.Method.Name == "GetContext");
        }
Пример #13
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            return(new ContainsContext(buildInfo.Parent, methodCall, sequence));
        }
Пример #14
0
 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
 {
     return(methodCall.IsSameGenericMethod(Methods.LinqToDB.IgnoreFilters));
 }
Пример #15
0
        // Returns
        // (ParentType p) => dc.GetTable<ObjectType>().Where(...)
        // (ParentType p) => dc.GetTable<ObjectType>().Where(...).DefaultIfEmpty
        public static LambdaExpression CreateAssociationQueryLambda(ExpressionBuilder builder, AccessorMember onMember, AssociationDescriptor association,
                                                                    Type parentOriginalType,
                                                                    Type parentType,
                                                                    Type objectType, bool inline, bool enforceDefault,
                                                                    List <LoadWithInfo[]>?loadWith, out bool isLeft)
        {
            var dataContextConstant = Expression.Constant(builder.DataContext, builder.DataContext.GetType());

            // We are trying to keep fast cache hit behaviour, so cache check should be added only if needed
            //
            bool shouldAddCacheCheck = false;

            bool cacheCheckAdded = false;

            LambdaExpression?definedQueryMethod = null;

            if (association.HasQueryMethod())
            {
                // here we tell for Expression Comparer to compare optimized Association expressions
                //
                definedQueryMethod = (LambdaExpression)builder.AddQueryableMemberAccessors(onMember, builder.DataContext, (mi, dc) =>
                {
                    var queryLambda         = association.GetQueryMethod(parentType, objectType) ?? throw new InvalidOperationException();
                    var optimizationContext = new ExpressionTreeOptimizationContext(dc);
                    var optimizedExpr       = optimizationContext.ExposeExpression(queryLambda);
                    optimizedExpr           = optimizationContext.ExpandQueryableMethods(optimizedExpr);
                    optimizedExpr           = optimizedExpr.OptimizeExpression() !;
                    return(optimizedExpr);
                });

                cacheCheckAdded = true;

                var parameterMatch = new Dictionary <ParameterExpression, Expression>();
                if (onMember.Arguments == null)
                {
                    if (definedQueryMethod.Parameters.Count > 1 && typeof(IDataContext).IsSameOrParentOf(definedQueryMethod.Parameters[1].Type))
                    {
                        parameterMatch.Add(definedQueryMethod.Parameters[1], dataContextConstant);
                    }
                }
                else
                {
                    var definedCount   = definedQueryMethod.Parameters.Count;
                    var argumentsCount = onMember.Arguments.Count;
                    var diff           = definedCount - argumentsCount;
                    for (int i = definedCount - 1; i >= diff; i--)
                    {
                        parameterMatch.Add(definedQueryMethod.Parameters[i], onMember.Arguments[i - diff]);
                    }
                }

                var body = definedQueryMethod.Body.Transform(e =>
                {
                    if (e.NodeType == ExpressionType.Parameter &&
                        parameterMatch.TryGetValue((ParameterExpression)e, out var newExpression))
                    {
                        return(newExpression);
                    }

                    return(e);
                });

                definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters[0]);
            }

            var shouldAddDefaultIfEmpty = enforceDefault;

            if (definedQueryMethod == null)
            {
                var parentParam = Expression.Parameter(parentType, "parent");
                var childParam  = Expression.Parameter(objectType, association.AliasName);

                var parentAccessor = TypeAccessor.GetAccessor(parentType);
                var childAccessor  = TypeAccessor.GetAccessor(objectType);

                Expression?predicate = null;
                for (var i = 0; i < association.ThisKey.Length; i++)
                {
                    var parentName   = association.ThisKey[i];
                    var parentMember = parentAccessor.Members.Find(m => m.MemberInfo.Name == parentName);

                    if (parentMember == null)
                    {
                        throw new LinqException("Association key '{0}' not found for type '{1}.", parentName,
                                                parentType);
                    }

                    var childName   = association.OtherKey[i];
                    var childMember = childAccessor.Members.Find(m => m.MemberInfo.Name == childName);

                    if (childMember == null)
                    {
                        throw new LinqException("Association key '{0}' not found for type '{1}.", childName,
                                                objectType);
                    }

                    var current = ExpressionBuilder.Equal(builder.MappingSchema,
                                                          Expression.MakeMemberAccess(parentParam, parentMember.MemberInfo),
                                                          Expression.MakeMemberAccess(childParam, childMember.MemberInfo));

                    predicate = predicate == null ? current : Expression.AndAlso(predicate, current);
                }

                var expressionPredicate = association.GetPredicate(parentType, objectType);

                if (expressionPredicate != null)
                {
                    shouldAddDefaultIfEmpty = true;
                    shouldAddCacheCheck     = true;

                    var replacedBody = expressionPredicate.GetBody(parentParam, childParam);

                    predicate = predicate == null ? replacedBody : Expression.AndAlso(predicate, replacedBody);
                }

                if (predicate == null)
                {
                    throw new LinqException("Can not generate Association predicate");
                }

                if (inline && !shouldAddDefaultIfEmpty)
                {
                    var ed = builder.MappingSchema.GetEntityDescriptor(objectType);
                    if (ed.QueryFilterFunc != null)
                    {
                        shouldAddDefaultIfEmpty = true;
                        shouldAddCacheCheck     = true;
                    }
                }

                var queryParam = Expression.Call(Methods.LinqToDB.GetTable.MakeGenericMethod(objectType), dataContextConstant);

                var        filterLambda = Expression.Lambda(predicate, childParam);
                Expression body         = Expression.Call(Methods.Queryable.Where.MakeGenericMethod(objectType), queryParam,
                                                          filterLambda);

                definedQueryMethod = Expression.Lambda(body, parentParam);
            }
            else
            {
                shouldAddDefaultIfEmpty = true;
                var bodyExpression = definedQueryMethod.Body.Unwrap();
                if (bodyExpression.NodeType == ExpressionType.Call)
                {
                    var mc = (MethodCallExpression)bodyExpression;
                    if (mc.IsSameGenericMethod(Methods.Queryable.DefaultIfEmpty, Methods.Queryable.DefaultIfEmptyValue))
                    {
                        shouldAddDefaultIfEmpty = false;
                    }
                }
            }

            if (!cacheCheckAdded && shouldAddCacheCheck)
            {
                // here we tell for Expression Comparer to compare optimized Association expressions
                //
                var closureExpr = definedQueryMethod;
                definedQueryMethod = (LambdaExpression)builder.AddQueryableMemberAccessors(onMember, builder.DataContext, (mi, dc) =>
                {
                    var optimizationContext = new ExpressionTreeOptimizationContext(dc);
                    var optimizedExpr       = optimizationContext.ExposeExpression(closureExpr);
                    optimizedExpr           = optimizationContext.ExpandQueryableMethods(optimizedExpr);
                    optimizedExpr           = optimizedExpr.OptimizeExpression() !;
                    return(optimizedExpr);
                });
            }

            if (loadWith != null)
            {
                var associationLoadWith = GetLoadWith(loadWith)?
                                          .FirstOrDefault(li => li.Info.MemberInfo == association.MemberInfo);

                if (associationLoadWith != null &&
                    (associationLoadWith.Info.MemberFilter != null || associationLoadWith.Info.FilterFunc != null))
                {
                    var body = definedQueryMethod.Body.Unwrap();

                    var memberFilter = associationLoadWith.Info.MemberFilter;
                    if (memberFilter != null)
                    {
                        var elementType = EagerLoading.GetEnumerableElementType(memberFilter.Parameters[0].Type,
                                                                                builder.MappingSchema);
                        var filtered   = Expression.Convert(body, typeof(IEnumerable <>).MakeGenericType(elementType));
                        var filterBody = memberFilter.GetBody(filtered);
                        body = Expression.Call(
                            Methods.Queryable.AsQueryable.MakeGenericMethod(objectType), filterBody);
                    }

                    var loadWithFunc = associationLoadWith.Info.FilterFunc;

                    if (loadWithFunc != null)
                    {
                        loadWithFunc = loadWithFunc.Unwrap();
                        if (loadWithFunc is LambdaExpression lambda)
                        {
                            body = lambda.GetBody(body);
                        }
                        else
                        {
                            var filterDelegate = loadWithFunc.EvaluateExpression <Delegate>() ??
                                                 throw new LinqException("Cannot convert filter function '{loadWithFunc}' to Delegate.");

                            var arumentType = filterDelegate.GetType().GetGenericArguments()[0].GetGenericArguments()[0];
                            // check for fake argument q => q
                            if (arumentType.IsSameOrParentOf(objectType))
                            {
                                var query    = ExpressionQueryImpl.CreateQuery(objectType, builder.DataContext, body);
                                var filtered = (IQueryable)filterDelegate.DynamicInvoke(query) !;
                                body = filtered.Expression;
                            }
                        }
                    }

                    definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters);
                }

                if (associationLoadWith?.NextLoadWith != null && associationLoadWith.NextLoadWith.Count > 0)
                {
                    definedQueryMethod = (LambdaExpression)EnrichTablesWithLoadWith(builder.DataContext, definedQueryMethod, objectType,
                                                                                    associationLoadWith.NextLoadWith, builder.MappingSchema);
                }
            }

            if (parentOriginalType != parentType)
            {
                // add discriminator filter
                var ed = builder.MappingSchema.GetEntityDescriptor(parentOriginalType);
                foreach (var inheritanceMapping in ed.InheritanceMapping)
                {
                    if (inheritanceMapping.Type == parentType)
                    {
                        var objParam     = Expression.Parameter(objectType, "o");
                        var filterLambda = Expression.Lambda(ExpressionBuilder.Equal(builder.MappingSchema,
                                                                                     Expression.MakeMemberAccess(definedQueryMethod.Parameters[0], inheritanceMapping.Discriminator.MemberInfo),
                                                                                     Expression.Constant(inheritanceMapping.Code)), objParam);

                        var body = definedQueryMethod.Body.Unwrap();
                        body = Expression.Call(Methods.Queryable.Where.MakeGenericMethod(objectType),
                                               body, filterLambda);
                        definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters);

                        shouldAddDefaultIfEmpty = true;
                        break;
                    }
                }
            }

            if (inline && shouldAddDefaultIfEmpty)
            {
                var body = definedQueryMethod.Body.Unwrap();
                body = Expression.Call(Methods.Queryable.DefaultIfEmpty.MakeGenericMethod(objectType), body);
                definedQueryMethod = Expression.Lambda(body, definedQueryMethod.Parameters);
                isLeft             = true;
            }
            else
            {
                isLeft = false;
            }

            definedQueryMethod = (LambdaExpression)builder.ConvertExpressionTree(definedQueryMethod);
            definedQueryMethod = (LambdaExpression)builder.ConvertExpression(definedQueryMethod);
            definedQueryMethod = (LambdaExpression)definedQueryMethod.OptimizeExpression() !;

            return(definedQueryMethod);
        }
Пример #16
0
        IBuildContext GetSubQueryContext(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo,
                                         SelectQuery sql,
                                         LambdaExpression innerKeyLambda,
                                         Expression outerKeySelector,
                                         Expression innerKeySelector,
                                         IBuildContext outerKeyContext)
        {
            var subQueryContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()));

            subQueryContext = new SubQueryContext(subQueryContext);

            var subQueryParent     = subQueryContext.Parent;
            var subQueryKeyContext = new ExpressionContext(buildInfo.Parent, subQueryContext, innerKeyLambda);

            // Process SubQuery.
            //
            var subQuerySql = ((SubQueryContext)subQueryContext).SelectQuery;

            // Make join and where for the counter.
            //
            if (outerKeySelector.NodeType == ExpressionType.New)
            {
                var new1 = (NewExpression)outerKeySelector;
                var new2 = (NewExpression)innerKeySelector;

                for (var i = 0; i < new1.Arguments.Count; i++)
                {
                    var arg1 = new1.Arguments[i];
                    var arg2 = new2.Arguments[i];

                    BuildSubQueryJoin(builder, outerKeyContext, arg1, arg2, subQueryKeyContext, subQuerySql);
                }
            }
            else if (outerKeySelector.NodeType == ExpressionType.MemberInit)
            {
                var mi1 = (MemberInitExpression)outerKeySelector;
                var mi2 = (MemberInitExpression)innerKeySelector;

                for (var i = 0; i < mi1.Bindings.Count; i++)
                {
                    if (mi1.Bindings[i].Member != mi2.Bindings[i].Member)
                    {
                        throw new LinqException("List of member inits does not match for entity type '{0}'.".Args(outerKeySelector.Type));
                    }

                    var arg1 = ((MemberAssignment)mi1.Bindings[i]).Expression;
                    var arg2 = ((MemberAssignment)mi2.Bindings[i]).Expression;

                    BuildSubQueryJoin(builder, outerKeyContext, arg1, arg2, subQueryKeyContext, subQuerySql);
                }
            }
            else
            {
                BuildSubQueryJoin(builder, outerKeyContext, outerKeySelector, innerKeySelector, subQueryKeyContext, subQuerySql);
            }

            builder.ReplaceParent(subQueryKeyContext, subQueryParent);

            subQuerySql.ParentSelect = sql;
            subQuerySql.Select.Columns.Clear();

            return(subQueryContext);
        }
Пример #17
0
 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
 {
     return(methodCall.Arguments.Count == 2 && methodCall.IsQueryable(MethodNames));
 }
Пример #18
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var isGroup      = methodCall.Method.Name == "GroupJoin";
            var outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0], buildInfo.SelectQuery));
            var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()));

            var context = new SubQueryContext(outerContext);

            innerContext = isGroup ? new GroupJoinSubQueryContext(innerContext) : new SubQueryContext(innerContext);

            var join = isGroup ? innerContext.SelectQuery.WeakLeftJoin() : innerContext.SelectQuery.InnerJoin();
            var sql  = context.SelectQuery;

            sql.From.Tables[0].Joins.Add(join.JoinedTable);

            var selector = (LambdaExpression)methodCall.Arguments[4].Unwrap();

            context.SetAlias(selector.Parameters[0].Name);
            innerContext.SetAlias(selector.Parameters[1].Name);

            var outerKeyLambda = ((LambdaExpression)methodCall.Arguments[2].Unwrap());
            var innerKeyLambda = ((LambdaExpression)methodCall.Arguments[3].Unwrap());

            var outerKeySelector = outerKeyLambda.Body.Unwrap();
            var innerKeySelector = innerKeyLambda.Body.Unwrap();

            var outerParent = context.Parent;
            var innerParent = innerContext.Parent;

            var outerKeyContext = new ExpressionContext(buildInfo.Parent, context, outerKeyLambda);
            var innerKeyContext = new InnerKeyContext(buildInfo.Parent, innerContext, innerKeyLambda);

            // Make join and where for the counter.
            //
            if (outerKeySelector.NodeType == ExpressionType.New)
            {
                var new1 = (NewExpression)outerKeySelector;
                var new2 = (NewExpression)innerKeySelector;

                for (var i = 0; i < new1.Arguments.Count; i++)
                {
                    var arg1 = new1.Arguments[i];
                    var arg2 = new2.Arguments[i];

                    BuildJoin(builder, join, outerKeyContext, arg1, innerKeyContext, arg2);
                }
            }
            else if (outerKeySelector.NodeType == ExpressionType.MemberInit)
            {
                var mi1 = (MemberInitExpression)outerKeySelector;
                var mi2 = (MemberInitExpression)innerKeySelector;

                for (var i = 0; i < mi1.Bindings.Count; i++)
                {
                    if (mi1.Bindings[i].Member != mi2.Bindings[i].Member)
                    {
                        throw new LinqException("List of member inits does not match for entity type '{0}'.".Args(outerKeySelector.Type));
                    }

                    var arg1 = ((MemberAssignment)mi1.Bindings[i]).Expression;
                    var arg2 = ((MemberAssignment)mi2.Bindings[i]).Expression;

                    BuildJoin(builder, join, outerKeyContext, arg1, innerKeyContext, arg2);
                }
            }
            else
            {
                BuildJoin(builder, join, outerKeyContext, outerKeySelector, innerKeyContext, innerKeySelector);
            }

            builder.ReplaceParent(outerKeyContext, outerParent);
            builder.ReplaceParent(innerKeyContext, innerParent);

            if (isGroup)
            {
                var inner = (GroupJoinSubQueryContext)innerContext;

                inner.Join = join.JoinedTable;
                inner.GetSubQueryContext = () =>
                                           GetSubQueryContext(builder, methodCall, buildInfo, sql,
                                                              innerKeyLambda, outerKeySelector, innerKeySelector, outerKeyContext);

                return(new GroupJoinContext(
                           buildInfo.Parent, selector, context, inner, methodCall.Arguments[1], outerKeyLambda, innerKeyLambda));
            }

            return(new JoinContext(buildInfo.Parent, selector, context, innerContext)
#if DEBUG
            {
                MethodCall = methodCall
            }
#endif
                   );
        }
Пример #19
0
                public Expression GetGrouping(GroupByContext context)
                {
                    if (Configuration.Linq.GuardGrouping && !context._isGroupingGuardDisabled)
                    {
                        if (context.Element.Lambda.Parameters.Count == 1 &&
                            context.Element.Body == context.Element.Lambda.Parameters[0])
                        {
                            var ex = new LinqToDBException(
                                "You should explicitly specify selected fields for server-side GroupBy() call or add AsEnumerable() call before GroupBy() to perform client-side grouping.\n" +
                                "Set Configuration.Linq.GuardGrouping = false to disable this check.\n" +
                                "Additionally this guard exception can be disabled by extension GroupBy(...).DisableGuard().\n" +
                                "NOTE! By disabling this guard you accept additional Database Connection(s) to the server for processing such requests."
                                )
                            {
                                HelpLink = "https://github.com/linq2db/linq2db/issues/365"
                            };

                            throw ex;
                        }
                    }

                    var parameters = context.Builder.CurrentSqlParameters
                                     .Select((p, i) => new { p, i })
                                     .ToDictionary(_ => _.p.Expression, _ => _.i);
                    var paramArray = Expression.Parameter(typeof(object[]), "ps");

                    var groupExpression = context._sequenceExpr.Transform(e =>
                    {
                        if (parameters.TryGetValue(e, out var idx))
                        {
                            return
                            (Expression.Convert(
                                 Expression.ArrayIndex(paramArray, Expression.Constant(idx)),
                                 e.Type));
                        }

                        return(e);
                    });

                    var keyParam = Expression.Parameter(typeof(TKey), "key");

// ReSharper disable AssignNullToNotNullAttribute

                    var expr = Expression.Call(
                        null,
                        MemberHelper.MethodOf(() => Queryable.Where(null, (Expression <Func <TSource, bool> >)null !)),
                        groupExpression,
                        Expression.Lambda <Func <TSource, bool> >(
                            ExpressionBuilder.Equal(context.Builder.MappingSchema, context._key.Lambda.Body, keyParam),
                            new[] { context._key.Lambda.Parameters[0] }));

                    expr = Expression.Call(
                        null,
                        MemberHelper.MethodOf(() => Queryable.Select(null, (Expression <Func <TSource, TElement> >)null !)),
                        expr,
                        context.Element.Lambda);

// ReSharper restore AssignNullToNotNullAttribute

                    var lambda = Expression.Lambda <Func <IDataContext, TKey, object?[]?, IQueryable <TElement> > >(
                        Expression.Convert(expr, typeof(IQueryable <TElement>)),
                        Expression.Parameter(typeof(IDataContext), "ctx"),
                        keyParam,
                        paramArray);

                    var itemReader = CompiledQuery.Compile(lambda);
                    var keyExpr    = context._key.BuildExpression(null, 0, false);

                    return(Expression.Call(
                               null,
                               MemberHelper.MethodOf(() => GetGrouping(null !, null !, default !, null !)),
Пример #20
0
        protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var functions = Sql.ExtensionAttribute.GetExtensionAttributes(methodCall, builder.MappingSchema);

            return(functions.Any());
        }
 public CteTableContext(ExpressionBuilder builder, BuildInfo buildInfo, CteClause cte, Expression cteExpression)
     : base(builder, buildInfo, new SqlCteTable(builder.MappingSchema, cte))
 {
     _cte           = cte;
     _cteExpression = cteExpression;
 }
Пример #22
0
 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
 {
     return(methodCall.IsSameGenericMethod(DataExtensions.SelectQueryMethodInfo));
 }
Пример #23
0
        static IEnumerable <MemberInfo> GetAssociations(ExpressionBuilder builder, Expression expression, Expression?stopExpression)
        {
            MemberInfo?lastMember = null;

            for (;;)
            {
                if (stopExpression == expression)
                {
                    yield break;
                }

                switch (expression.NodeType)
                {
                case ExpressionType.Parameter:
                    if (lastMember == null)
                    {
                        goto default;
                    }
                    yield break;

                case ExpressionType.Call:
                {
                    var cexpr = (MethodCallExpression)expression;

                    if (cexpr.Method.IsSqlPropertyMethodEx())
                    {
                        foreach (var assoc in GetAssociations(builder, builder.ConvertExpression(expression), stopExpression))
                        {
                            yield return(assoc);
                        }

                        yield break;
                    }

                    if (lastMember == null)
                    {
                        goto default;
                    }

                    var expr = cexpr.Object;

                    if (expr == null)
                    {
                        if (cexpr.Arguments.Count == 0)
                        {
                            goto default;
                        }

                        expr = cexpr.Arguments[0];
                    }

                    if (expr.NodeType != ExpressionType.MemberAccess)
                    {
                        goto default;
                    }

                    var member = ((MemberExpression)expr).Member;
                    var mtype  = member.GetMemberType();

                    if (lastMember.ReflectedType != mtype.GetItemType())
                    {
                        goto default;
                    }

                    expression = expr;

                    break;
                }

                case ExpressionType.MemberAccess:
                {
                    var mexpr  = (MemberExpression)expression;
                    var member = lastMember = mexpr.Member;
                    var attr   = builder.MappingSchema.GetAttribute <AssociationAttribute>(member.ReflectedType !, member);
                    if (attr == null)
                    {
                        member = mexpr.Expression !.Type.GetMemberEx(member) !;
                        attr   = builder.MappingSchema.GetAttribute <AssociationAttribute>(mexpr.Expression.Type, member);
                    }
                    if (attr == null)
                    {
                        throw new LinqToDBException($"Member '{expression}' is not an association.");
                    }

                    yield return(member);

                    expression = mexpr.Expression !;

                    break;
                }

                case ExpressionType.ArrayIndex:
                {
                    expression = ((BinaryExpression)expression).Left;
                    break;
                }

                case ExpressionType.Extension:
                {
                    if (expression is GetItemExpression getItemExpression)
                    {
                        expression = getItemExpression.Expression;
                        break;
                    }

                    goto default;
                }

                case ExpressionType.Convert:
                {
                    expression = ((UnaryExpression)expression).Operand;
                    break;
                }

                default:
                {
                    throw new LinqToDBException($"Expression '{expression}' is not an association.");
                }
                }
            }
        }
        static IBuildContext BuildCteContext(ExpressionBuilder builder, BuildInfo buildInfo)
        {
            var methodCall = (MethodCallExpression)buildInfo.Expression;

            Expression bodyExpr;
            IQueryable query       = null;
            string     name        = null;
            bool       isRecursive = false;

            switch (methodCall.Arguments.Count)
            {
            case 1:
                bodyExpr = methodCall.Arguments[0].Unwrap();
                break;

            case 2:
                bodyExpr = methodCall.Arguments[0].Unwrap();
                name     = methodCall.Arguments[1].EvaluateExpression() as string;
                break;

            case 3:
                query       = methodCall.Arguments[0].EvaluateExpression() as IQueryable;
                bodyExpr    = methodCall.Arguments[1].Unwrap();
                name        = methodCall.Arguments[2].EvaluateExpression() as string;
                isRecursive = true;
                break;

            default:
                throw new InvalidOperationException();
            }

            builder.RegisterCte(query, bodyExpr, () => new CteClause(null, bodyExpr.Type.GetGenericArgumentsEx()[0], isRecursive, name));

            var cte = builder.BuildCte(bodyExpr,
                                       cteClause =>
            {
                var info     = new BuildInfo(buildInfo, bodyExpr, new SelectQuery());
                var sequence = builder.BuildSequence(info);

                if (cteClause == null)
                {
                    cteClause = new CteClause(sequence.SelectQuery, bodyExpr.Type.GetGenericArgumentsEx()[0], isRecursive, name);
                }
                else
                {
                    cteClause.Body = sequence.SelectQuery;
                    cteClause.Name = name;
                }

                return(Tuple.Create(cteClause, sequence));
            }
                                       );

            var cteBuildInfo = new BuildInfo(buildInfo, bodyExpr, buildInfo.SelectQuery);
            var cteContext   = new CteTableContext(builder, cteBuildInfo, cte.Item1, bodyExpr);

            // populate all fields
            if (isRecursive)
            {
                cteContext.ConvertToSql(null, 0, ConvertFlags.All);
            }

            return(cteContext);
        }
Пример #25
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            switch (methodCall.Arguments.Count)
            {
            case 1:                      // int Update<T>(this IUpdateable<T> source)
                CheckAssociation(sequence);
                break;

            case 2:                      // int Update<T>(this IQueryable<T> source, Expression<Func<T,T>> setter)
            {
                CheckAssociation(sequence);

                BuildSetter(
                    builder,
                    buildInfo,
                    (LambdaExpression)methodCall.Arguments[1].Unwrap(),
                    sequence,
                    sequence.SelectQuery.Update.Items,
                    sequence);
                break;
            }

            case 3:
            {
                var expr = methodCall.Arguments[1].Unwrap();

                if (expr is LambdaExpression)
                {
                    CheckAssociation(sequence);

                    // int Update<T>(this IQueryable<T> source, Expression<Func<T,bool>> predicate, Expression<Func<T,T>> setter)
                    //
                    sequence = builder.BuildWhere(buildInfo.Parent, sequence, (LambdaExpression)methodCall.Arguments[1].Unwrap(), false);

                    BuildSetter(
                        builder,
                        buildInfo,
                        (LambdaExpression)methodCall.Arguments[2].Unwrap(),
                        sequence,
                        sequence.SelectQuery.Update.Items,
                        sequence);
                }
                else
                {
                    // static int Update<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter)
                    //
                    var into = builder.BuildSequence(new BuildInfo(buildInfo, expr, new SelectQuery()));

                    sequence.ConvertToIndex(null, 0, ConvertFlags.All);
                    new SelectQueryOptimizer(builder.DataContextInfo.SqlProviderFlags, sequence.SelectQuery)
                    .ResolveWeakJoins(new List <ISqlTableSource>());
                    sequence.SelectQuery.Select.Columns.Clear();

                    BuildSetter(
                        builder,
                        buildInfo,
                        (LambdaExpression)methodCall.Arguments[2].Unwrap(),
                        into,
                        sequence.SelectQuery.Update.Items,
                        sequence);

                    var sql = sequence.SelectQuery;

                    sql.Select.Columns.Clear();

                    foreach (var item in sql.Update.Items)
                    {
                        sql.Select.Columns.Add(new SelectQuery.Column(sql, item.Expression));
                    }

                    sql.Update.Table = ((TableBuilder.TableContext)into).SqlTable;
                }

                break;
            }
            }

            sequence.SelectQuery.QueryType = QueryType.Update;

            return(new UpdateContext(buildInfo.Parent, sequence));
        }
Пример #26
0
 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
 {
     return(methodCall.IsQueryable("Insert", "InsertWithIdentity"));
 }
Пример #27
0
 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
 {
     return(methodCall.IsQueryable(nameof(LinqExtensions.TagQuery)));
 }
Пример #28
0
        protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
        {
            var sequence = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0]));

            var isSubQuery = sequence.SelectQuery.Select.IsDistinct;

            if (isSubQuery)
            {
                sequence = new SubQueryContext(sequence);
            }

            switch (methodCall.Arguments.Count)
            {
            case 1:
                // static int Insert<T>              (this IValueInsertable<T> source)
                // static int Insert<TSource,TTarget>(this ISelectInsertable<TSource,TTarget> source)
            {
                foreach (var item in sequence.SelectQuery.Insert.Items)
                {
                    sequence.SelectQuery.Select.Expr(item.Expression);
                }
                break;
            }

            case 2:                      // static int Insert<T>(this Table<T> target, Expression<Func<T>> setter)
            {
                UpdateBuilder.BuildSetter(
                    builder,
                    buildInfo,
                    (LambdaExpression)methodCall.Arguments[1].Unwrap(),
                    sequence,
                    sequence.SelectQuery.Insert.Items,
                    sequence);

                sequence.SelectQuery.Insert.Into = ((TableBuilder.TableContext)sequence).SqlTable;
                sequence.SelectQuery.From.Tables.Clear();

                break;
            }

            case 3:                      // static int Insert<TSource,TTarget>(this IQueryable<TSource> source, Table<TTarget> target, Expression<Func<TSource,TTarget>> setter)
            {
                var into = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery()));

                UpdateBuilder.BuildSetter(
                    builder,
                    buildInfo,
                    (LambdaExpression)methodCall.Arguments[2].Unwrap(),
                    into,
                    sequence.SelectQuery.Insert.Items,
                    sequence);

                sequence.SelectQuery.Select.Columns.Clear();

                foreach (var item in sequence.SelectQuery.Insert.Items)
                {
                    sequence.SelectQuery.Select.Columns.Add(new SelectQuery.Column(sequence.SelectQuery, item.Expression));
                }

                sequence.SelectQuery.Insert.Into = ((TableBuilder.TableContext)into).SqlTable;

                break;
            }
            }

            var insert = sequence.SelectQuery.Insert;

            var q = insert.Into.Fields.Values
                    .Except(insert.Items.Select(e => e.Column))
                    .OfType <SqlField>()
                    .Where(f => f.IsIdentity);

            foreach (var field in q)
            {
                var expr = builder.DataContext.CreateSqlProvider().GetIdentityExpression(insert.Into);

                if (expr != null)
                {
                    insert.Items.Insert(0, new SelectQuery.SetExpression(field, expr));

                    if (methodCall.Arguments.Count == 3)
                    {
                        sequence.SelectQuery.Select.Columns.Insert(0, new SelectQuery.Column(sequence.SelectQuery, insert.Items[0].Expression));
                    }
                }
            }

            sequence.SelectQuery.QueryType           = QueryType.Insert;
            sequence.SelectQuery.Insert.WithIdentity = methodCall.Method.Name == "InsertWithIdentity";

            return(new InsertContext(buildInfo.Parent, sequence, sequence.SelectQuery.Insert.WithIdentity));
        }
Пример #29
0
 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
 {
     return(methodCall.IsQueryable("OfType"));
 }
Пример #30
0
 protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo)
 {
     return(methodCall.IsSameGenericMethod(_supportedMethods));
 }