protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return(methodCall.IsSameGenericMethod(Methods.LinqToDB.DisableGuard)); }
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)); }
protected override SequenceConvertInfo Convert( ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo, ParameterExpression param) { return(null); }
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)); }
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); }
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)); }
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); }
public bool IsSequence(ExpressionBuilder builder, BuildInfo buildInfo) { return(builder.IsSequence(new BuildInfo(buildInfo, ((MethodCallExpression)buildInfo.Expression).Arguments[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")); }
public IBuildContext BuildSequence(ExpressionBuilder builder, BuildInfo buildInfo) { var call = (MethodCallExpression)buildInfo.Expression; return(new Context(builder.BuildSequence(new BuildInfo(buildInfo, call.Arguments[0])))); }
public SequenceConvertInfo?Convert(ExpressionBuilder builder, BuildInfo buildInfo, ParameterExpression?param) { return(null); }
public bool CanBuild(ExpressionBuilder builder, BuildInfo buildInfo) { var call = buildInfo.Expression as MethodCallExpression; return(call != null && call.Method.Name == "GetContext"); }
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)); }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return(methodCall.IsSameGenericMethod(Methods.LinqToDB.IgnoreFilters)); }
// 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); }
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); }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return(methodCall.Arguments.Count == 2 && methodCall.IsQueryable(MethodNames)); }
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 ); }
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 !)),
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; }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return(methodCall.IsSameGenericMethod(DataExtensions.SelectQueryMethodInfo)); }
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); }
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)); }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return(methodCall.IsQueryable("Insert", "InsertWithIdentity")); }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return(methodCall.IsQueryable(nameof(LinqExtensions.TagQuery))); }
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)); }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return(methodCall.IsQueryable("OfType")); }
protected override bool CanBuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { return(methodCall.IsSameGenericMethod(_supportedMethods)); }