IBuildContext ApplyQueryFilters(ExpressionBuilder builder, BuildInfo buildInfo, MemberInfo?memberInfo, TableContext tableContext) { var entityType = tableContext.ObjectType; if (builder.IsFilterDisabled(entityType)) { return(tableContext); } var ed = builder.MappingSchema.GetEntityDescriptor(entityType); var filterFunc = ed.QueryFilterFunc; if (filterFunc == null) { return(tableContext); } if (memberInfo == null) { memberInfo = Methods.LinqToDB.GetTable.MakeGenericMethod(entityType); } var fakeQuery = ExpressionQueryImpl.CreateQuery(entityType, builder.DataContext, null); // Here we tell for Equality Comparer to compare optimized expressions // builder.AddQueryableMemberAccessors(new AccessorMember(memberInfo), builder.DataContext, (mi, dc) => { var filtered = (IQueryable)filterFunc.DynamicInvoke(fakeQuery, dc) !; // here we use light version of optimization, only for comparing trees var optimizationContext = new ExpressionTreeOptimizationContext(dc); var optimizedExpr = optimizationContext.ExposeExpression(filtered.Expression); optimizedExpr = optimizationContext.ExpandQueryableMethods(optimizedExpr); optimizedExpr = optimizedExpr.OptimizeExpression() !; return(optimizedExpr); }); var filtered = (IQueryable)filterFunc.DynamicInvoke(fakeQuery, builder.DataContext) !; var optimized = filtered.Expression; optimized = builder.ConvertExpressionTree(optimized); optimized = builder.ConvertExpression(optimized); optimized = optimized.OptimizeExpression() !; var refExpression = new ContextRefExpression(typeof(IQueryable <>).MakeGenericType(entityType), tableContext); var replaced = optimized.Replace(fakeQuery.Expression, refExpression); if (replaced == optimized) { throw new LinqException("Could not correct query result for processing."); } var context = builder.BuildSequence(new BuildInfo(buildInfo, replaced)); return(context); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { // UpdateWhenNotMatchedBySourceAnd(merge, searchCondition, setter) var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var statement = mergeContext.Merge; var operation = new SqlMergeOperationClause(MergeOperationType.UpdateBySource); statement.Operations.Add(operation); Expression predicate = methodCall.Arguments[1]; Expression setter = methodCall.Arguments[2]; UpdateBuilder.BuildSetter( builder, buildInfo, (LambdaExpression)setter.Unwrap(), mergeContext, operation.Items, mergeContext); if (!(predicate is ConstantExpression constPredicate) || constPredicate.Value != null) { var condition = (LambdaExpression)predicate.Unwrap(); var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap()); operation.Where = new SqlSearchCondition(); builder.BuildSearchCondition( new ExpressionContext(null, new[] { mergeContext.TargetContext }, condition), conditionExpr, operation.Where.Conditions, false); } return(mergeContext); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var statement = mergeContext.Merge; if (methodCall.Arguments.Count == 2) { // On<TTarget, TSource>(IMergeableOn<TTarget, TSource> merge, Expression<Func<TTarget, TSource, bool>> matchCondition) var predicate = methodCall.Arguments[1]; var condition = (LambdaExpression)predicate.Unwrap(); var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap()); mergeContext.AddTargetParameter(condition.Parameters[0]); mergeContext.AddSourceParameter(condition.Parameters[1]); builder.BuildSearchCondition( new ExpressionContext(null, new[] { mergeContext.TargetContext, mergeContext.SourceContext }, condition), conditionExpr, statement.On.Conditions, false); } else if (methodCall.Arguments.Count == 3) { var targetKeyLambda = ((LambdaExpression)methodCall.Arguments[1].Unwrap()); var sourceKeyLambda = ((LambdaExpression)methodCall.Arguments[2].Unwrap()); var targetKeySelector = targetKeyLambda.Body.Unwrap(); var sourceKeySelector = sourceKeyLambda.Body.Unwrap(); var targetKeyContext = new ExpressionContext(buildInfo.Parent, mergeContext.TargetContext, targetKeyLambda); var sourceKeyContext = new ExpressionContext(buildInfo.Parent, mergeContext.SourceContext, sourceKeyLambda); if (targetKeySelector.NodeType == ExpressionType.New) { var new1 = (NewExpression)targetKeySelector; var new2 = (NewExpression)sourceKeySelector; for (var i = 0; i < new1.Arguments.Count; i++) { var arg1 = new1.Arguments[i]; var arg2 = new2.Arguments[i]; JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, arg1, sourceKeyContext, arg2); } } else if (targetKeySelector.NodeType == ExpressionType.MemberInit) { // TODO: migrate unordered members support to original code var mi1 = (MemberInitExpression)targetKeySelector; var mi2 = (MemberInitExpression)sourceKeySelector; if (mi1.Bindings.Count != mi2.Bindings.Count) { throw new LinqException($"List of member inits does not match for entity type '{targetKeySelector.Type}'."); } for (var i = 0; i < mi1.Bindings.Count; i++) { var binding2 = (MemberAssignment)mi2.Bindings.Where(b => b.Member == mi1.Bindings[i].Member).FirstOrDefault(); if (binding2 == null) { throw new LinqException($"List of member inits does not match for entity type '{targetKeySelector.Type}'."); } var arg1 = ((MemberAssignment)mi1.Bindings[i]).Expression; var arg2 = binding2.Expression; JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, arg1, sourceKeyContext, arg2); } } else { JoinBuilder.BuildJoin(builder, statement.On, targetKeyContext, targetKeySelector, sourceKeyContext, sourceKeySelector); } } else { // OnTargetKey<TTarget>(IMergeableOn<TTarget, TTarget> merge) var targetType = methodCall.Method.GetGenericArguments()[0]; var pTarget = Expression.Parameter(targetType, "t"); var pSource = Expression.Parameter(targetType, "s"); var targetDescriptor = builder.MappingSchema.GetEntityDescriptor(targetType); Expression?ex = null; for (var i = 0; i < targetDescriptor.Columns.Count; i++) { var column = targetDescriptor.Columns[i]; if (!column.IsPrimaryKey) { continue; } var expr = Expression.Equal( Expression.MakeMemberAccess(pTarget, column.MemberInfo), Expression.MakeMemberAccess(pSource, column.MemberInfo)); ex = ex != null?Expression.AndAlso(ex, expr) : expr; } if (ex == null) { throw new LinqToDBException("Method OnTargetKey() needs at least one primary key column"); } var condition = Expression.Lambda(ex, pTarget, pSource); builder.BuildSearchCondition( new ExpressionContext(null, new[] { mergeContext.TargetContext, mergeContext.SourceContext }, condition), ex, statement.On.Conditions, false); } mergeContext.SourceContext.MatchBuilt(); return(mergeContext); }
// 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.Enumerable.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); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0], buildInfo.SelectQuery)); var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); outerContext = new SubQueryContext(outerContext); innerContext = new SubQueryContext(innerContext); var selector = (LambdaExpression)methodCall.Arguments[methodCall.Arguments.Count - 1].Unwrap(); outerContext.SetAlias(selector.Parameters[0].Name); innerContext.SetAlias(selector.Parameters[1].Name); JoinType joinType; var conditionIndex = 2; switch (methodCall.Method.Name) { case "InnerJoin": joinType = JoinType.Inner; break; case "CrossJoin": joinType = JoinType.Inner; conditionIndex = -1; break; case "LeftJoin": joinType = JoinType.Left; break; case "RightJoin": joinType = JoinType.Right; break; case "FullJoin": joinType = JoinType.Full; break; default: conditionIndex = 3; var joinValue = (SqlJoinType)methodCall.Arguments[2].EvaluateExpression(); switch (joinValue) { case SqlJoinType.Inner: joinType = JoinType.Inner; break; case SqlJoinType.Left: joinType = JoinType.Left; break; case SqlJoinType.Right: joinType = JoinType.Right; break; case SqlJoinType.Full: joinType = JoinType.Full; break; default: throw new ArgumentOutOfRangeException(); } break; } if (conditionIndex != -1) { var condition = (LambdaExpression)methodCall.Arguments[conditionIndex].Unwrap(); var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap()); var join = new SqlFromClause.Join(joinType, innerContext.SelectQuery, null, false, Array <SqlFromClause.Join> .Empty); outerContext.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); builder.BuildSearchCondition( new ExpressionContext(null, new[] { outerContext, innerContext }, condition), conditionExpr, join.JoinedTable.Condition.Conditions, false); } else { outerContext.SelectQuery.From.Table(innerContext.SelectQuery); } return(new SelectContext(buildInfo.Parent, selector, outerContext, innerContext) #if DEBUG { MethodCall = methodCall } #endif ); }
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(); } bodyExpr = builder.ConvertExpression(bodyExpr); builder.RegisterCte(query, bodyExpr, () => new CteClause(null, bodyExpr.Type.GetGenericArguments()[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.GetGenericArguments()[0], isRecursive, name); } else { cteClause.Body = sequence.SelectQuery; cteClause.Name = name; } return(Tuple.Create(cteClause, (IBuildContext?)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 outerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var innerContext = builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[1], new SelectQuery())); JoinType joinType; var conditionIndex = 2; switch (methodCall.Method.Name) { case "InnerJoin": joinType = JoinType.Inner; break; case "CrossJoin": joinType = JoinType.Inner; conditionIndex = -1; break; case "LeftJoin": joinType = JoinType.Left; break; case "RightJoin": joinType = JoinType.Right; break; case "FullJoin": joinType = JoinType.Full; break; default: conditionIndex = 3; joinType = (SqlJoinType)methodCall.Arguments[2].EvaluateExpression() !switch { SqlJoinType.Inner => JoinType.Inner, SqlJoinType.Left => JoinType.Left, SqlJoinType.Right => JoinType.Right, SqlJoinType.Full => JoinType.Full, _ => throw new InvalidOperationException($"Unexpected join type: {(SqlJoinType)methodCall.Arguments[2].EvaluateExpression()!}") }; break; } if (joinType == JoinType.Right || joinType == JoinType.Full) { outerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, outerContext, null); } outerContext = new SubQueryContext(outerContext); if (joinType == JoinType.Left || joinType == JoinType.Full) { innerContext = new DefaultIfEmptyBuilder.DefaultIfEmptyContext(buildInfo.Parent, innerContext, null); } innerContext = new SubQueryContext(innerContext); var selector = (LambdaExpression)methodCall.Arguments[methodCall.Arguments.Count - 1].Unwrap(); outerContext.SetAlias(selector.Parameters[0].Name); innerContext.SetAlias(selector.Parameters[1].Name); var joinContext = new JoinContext(buildInfo.Parent, selector, outerContext, innerContext) #if DEBUG { Debug_MethodCall = methodCall } #endif ; if (conditionIndex != -1) { var condition = (LambdaExpression)methodCall.Arguments[conditionIndex].Unwrap(); var conditionExpr = condition.GetBody(selector.Parameters[0], selector.Parameters[1]); conditionExpr = builder.ConvertExpression(conditionExpr); var join = new SqlFromClause.Join(joinType, innerContext.SelectQuery, null, false, Array <SqlFromClause.Join> .Empty); outerContext.SelectQuery.From.Tables[0].Joins.Add(join.JoinedTable); builder.BuildSearchCondition( joinContext, conditionExpr, @join.JoinedTable.Condition.Conditions); } else { outerContext.SelectQuery.From.Table(innerContext.SelectQuery); } return(joinContext); }
protected override IBuildContext BuildMethodCall(ExpressionBuilder builder, MethodCallExpression methodCall, BuildInfo buildInfo) { var mergeContext = (MergeContext)builder.BuildSequence(new BuildInfo(buildInfo, methodCall.Arguments[0])); var statement = mergeContext.Merge; var operation = new SqlMergeOperationClause(MergeOperationType.Insert); statement.Operations.Add(operation); var predicate = methodCall.Arguments[1]; var setter = methodCall.Arguments[2]; if (!setter.IsNullValue()) { var setterExpression = (LambdaExpression)setter.Unwrap(); mergeContext.AddSourceParameter(setterExpression.Parameters[0]); UpdateBuilder.BuildSetterWithContext( builder, buildInfo, setterExpression, mergeContext.TargetContext, operation.Items, mergeContext.SourceContext); } else { // build setters like QueryRunner.Insert var sqlTable = (SqlTable)statement.Target.Source; var param = Expression.Parameter(sqlTable.ObjectType, "s"); foreach (var field in sqlTable.Fields) { if (field.IsInsertable) { var expression = LinqToDB.Expressions.Extensions.GetMemberGetter(field.ColumnDescriptor.MemberInfo, param); var tgtExpr = mergeContext.TargetContext.ConvertToSql(builder.ConvertExpression(expression), 1, ConvertFlags.Field)[0].Sql; var srcExpr = mergeContext.SourceContext.ConvertToSql(builder.ConvertExpression(expression), 1, ConvertFlags.Field)[0].Sql; operation.Items.Add(new SqlSetExpression(tgtExpr, srcExpr)); } else if (field.IsIdentity) { var expr = builder.DataContext.CreateSqlProvider().GetIdentityExpression(sqlTable); if (expr != null) { operation.Items.Add(new SqlSetExpression(field, expr)); } } } } if (!predicate.IsNullValue()) { var condition = (LambdaExpression)predicate.Unwrap(); var conditionExpr = builder.ConvertExpression(condition.Body.Unwrap()); operation.Where = new SqlSearchCondition(); builder.BuildSearchCondition( new ExpressionContext(null, new[] { mergeContext.SourceContext }, condition), conditionExpr, operation.Where.Conditions); } return(mergeContext); }