public Expression Build(Expression source) { return(ReplaceVisitor.Replace( this.unary, x => x == this.root, x => source)); }
private static LambdaExpression CreatePostSelectManyResultSelector( Type innerType, Type groupJoinResultType, LambdaExpression originalResultSelector) { ParameterExpression groupJoinResult = Expression.Parameter(groupJoinResultType, "group"); ParameterExpression inner = Expression.Parameter(innerType, "inner"); // JoinGroup<,>.Outer property PropertyInfo outerElement = groupJoinResultType.GetProperty(JoinGroupOuter); Expression expression = originalResultSelector.Body; // Change the first parameter expression = ReplaceVisitor.Replace( expression, e => e == originalResultSelector.Parameters[0], e => Expression.Property(groupJoinResult, outerElement)); // Change the second parameter expression = ReplaceVisitor.Replace( expression, e => e == originalResultSelector.Parameters[1], e => inner); return(Expression.Lambda(expression, groupJoinResult, inner)); }
/// <summary> /// Reemplaza las referencias a las tablas de los join anteriores en un LATERAL subquery con RawSQL, devuelve el body reemplazado /// </summary> public static Expression ReplaceSubqueryLambda(LambdaExpression subquery, Expression leftParam, IEnumerable<ExprStrRawSql> replaceMembers) { var body = subquery.Body; var lateralParam = subquery.Parameters[0]; //Reemplazar el parametro del lateral con el leftParam: var bodyLeft = ReplaceVisitor.Replace(body, lateralParam, leftParam); return ReplaceSubqueryBody(bodyLeft, replaceMembers); }
/// <summary> /// Reemplaza las ocurrencias de replace members en el cuerpo de un subquery por llamadas al Sql.Raw /// </summary> /// <param name="body"></param> /// <param name="replaceMembers"></param> /// <returns></returns> public static Expression ReplaceSubqueryBody(Expression body, IEnumerable<ExprStrRawSql> replaceMembers) { //Sustituir con el replace members, con un RawSql Func<Expression, Expression> replaceRaw = (ex) => { var sql = ReplaceStringAliasMembers(ex, replaceMembers); if (sql == null) return ex; var ret = RawSqlTableExpr(ex.Type, sql); return ret; }; var bodyRaw = ReplaceVisitor.Replace(body, replaceRaw); return bodyRaw; }
/// <summary> /// (min, max, val) => bool /// Crea una expresión que devuelve true si val se encuentra dentro del rango (inclusivo en min y max), se aceptan valores nulos para min y max. /// </summary> /// <typeparam name="T">Tipo que debe de soportar los operadores de comparasión</typeparam> public static Expression <Func <T?, T?, T?, bool> > Range <T>() where T : struct { Expression <Func <int?, int?, int?, bool> > expr = (min, max, v) => (min == null || (v >= min)) && (max == null || (v <= max)); var subs = (Expression <Func <T?, T?, T?, bool> >)ReplaceVisitor.Replace(expr, new Dictionary <Expression, Expression>(), new Dictionary <Type, Type> { { typeof(int), typeof(T) } }, x => false); return(subs); }
/// <summary> /// Correlates all of the elements of an outer sequence with any matching elements of an inner sequence, based on matching keys. /// </summary> /// <typeparam name="TOuter">The type of the elements of the outer sequence.</typeparam> /// <typeparam name="TInner">The type of the elements of the inner sequence.</typeparam> /// <typeparam name="TKey">The type of the keys returned by the key selector functions.</typeparam> /// <typeparam name="TResult">The type of the result elements.</typeparam> /// <param name="outer">The outer sequence to join.</param> /// <param name="inner">The inner sequence to join.</param> /// <param name="outerKeySelector">A function to extract the join key from each element of the outer sequence.</param> /// <param name="innerKeySelector">A function to extract the join key from each element of the inner sequence.</param> /// <param name="resultSelector">A function to create a result element from two matching elements.</param> /// <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1" /> that has elements of type <typeparamref name="TResult" /> that are obtained by performing an outer join on the input sequences.</returns> /// <remarks><para>All elements of <paramref name="outer" /> will be selected, even if there is no matching element in <paramref name="inner" />; in that case, the value that is passed as the second parameter to <paramref name="resultSelector" /> will be the default value for <typeparamref name="TInner" />.</para></remarks> public static IQueryable <TResult> LeftJoin <TOuter, TInner, TKey, TResult>(this IQueryable <TOuter> outer, IEnumerable <TInner> inner, Expression <Func <TOuter, TKey> > outerKeySelector, Expression <Func <TInner, TKey> > innerKeySelector, Expression <Func <TOuter, TInner, TResult> > resultSelector) { if (outer == null) { throw new ArgumentNullException(nameof(outer)); } if (inner == null) { throw new ArgumentNullException(nameof(inner)); } if (outerKeySelector == null) { throw new ArgumentNullException(nameof(outerKeySelector)); } if (innerKeySelector == null) { throw new ArgumentNullException(nameof(innerKeySelector)); } if (resultSelector == null) { throw new ArgumentNullException(nameof(resultSelector)); } // TODO: this var outerExample = new { o = default(TOuter), i = (IEnumerable <TInner>)null }; var type = outerExample.GetType(); var expression = Expression.Parameter(type, "a"); return(outer .GroupJoin(inner, outerKeySelector, innerKeySelector, (o, i) => new { o, i }) .SelectMany( x => x.i.DefaultIfEmpty(), Lambda( outerExample, default(TInner), default(TResult), ReplaceVisitor.Replace(resultSelector.Body, resultSelector.Parameters[0], Expression.Property(expression, type.GetProperty("o"))), new[] { expression, resultSelector.Parameters[1] } ) )); }
/// <summary> /// Remplaza todas las referencias a un elemento del WITH con un SqlTableRefRaw /// </summary> public static Expression SubqueryRawSubs(Expression subquery, ParameterExpression repParam) { if (subquery == null) { return(null); } //Sustituir todo param.X o param por el nombre: var ret = ReplaceVisitor.Replace(subquery, expr => { if (typeof(IFromListItemTarget).GetTypeInfo().IsAssignableFrom(expr.Type.GetTypeInfo())) { var selectInt = expr.Type.GetTypeInfo().ImplementedInterfaces.Concat(new[] { expr.Type }).Where(x => x.GetTypeInfo().IsGenericType&& x.GetGenericTypeDefinition() == typeof(IFromListItemTarget <>)).FirstOrDefault(); if (selectInt == null) { throw new ArgumentException("Debe de ser un IFromListItemTarget<T>"); } var selectType = selectInt.GetTypeInfo().GenericTypeArguments[0]; if (expr is MemberExpression mem && CompareExpr.ExprEquals(mem.Expression, repParam)) { return(RawSqlTableRefExpr(selectType, $"\"{mem.Member.Name}\"")); }
/// <summary> /// Devuelve el resultado de aplicar una regla al niver superior de la expresión o la expresión original si la regla no se pudo aplicar a la expresión /// </summary> public static Expression GlobalApplyRule(Expression expr, RewriteRule rule, Func <Expression, Expression> visit) { if (rule.DebugName == "convertFromParam" || rule.DebugName == "fromParam") { ; } var parameters = rule.Find.Parameters; var pattBody = rule.Find.Body; PartialMatch partialMatch; try { partialMatch = GlobalMatch(expr, parameters, pattBody); } catch (Exception ex) { throw new ApplyRuleException("Error al obtener el match", rule.DebugName, expr, ex); } var match = PartialMatch.ToFullMatch(partialMatch, parameters); if (match == null) { return(expr); } if (rule.Condition != null && !rule.Condition(match, expr)) { return(expr); } //Sustituir la expresión: var ret = expr; var replaceLambda = rule.Replace; if (replaceLambda != null) { var subDic = replaceLambda.Parameters .Select((x, i) => (par: x, value: match.Args[i])) .ToDictionary(x => (Expression)x.par, x => x.value) ; Expression repRet; try { repRet = ReplaceVisitor.Replace(replaceLambda.Body, subDic, match.Types, x => false); } catch (Exception ex) { throw new ApplyRuleException("Error al reemplazar", rule.DebugName, expr, ex); } ret = repRet; } //Aplicar los transforms: { Expression nextRet; try { nextRet = ReplaceVisitor.Replace(ret, ex => { if (ex is MethodCallExpression call && call.Method.DeclaringType == typeof(RewriteSpecial) && call.Method.Name == nameof(RewriteSpecial.Transform)) { //Aplica el transform a la expresión: var arg = call.Arguments[0]; var func = ExprEval.EvalExpr <Func <Expression, Expression> >(call.Arguments[1]); var tResult = func.Value(arg); return(tResult); } return(ex); }); }
static IReadOnlyList<ExprAliasList> ExtractJoinAliasT(IFromListItem left, Expression leftParam, Expression rightParam, Expression onParam, Expression mapExprBody) { if (left == null) { return new[] { new ExprAliasList(new ExprRep[0], leftParam, onParam, null) }; } var subRet = ExtractJoinAliasTree(left); var leftOnParam = subRet[0].CurrParam; var currAliases = mapExprBody == null ? new ExpressionAlias[0] : ExtractAliases(mapExprBody); var ret = new List<ExprAliasList>(); //Si el left no tiene OnParam, debe de existir el lado izquierdo en el mapeo var existirLeft = leftOnParam == null; if (existirLeft && !currAliases.Any(x => x.Expr == leftParam)) { throw new ArgumentException($"El argumento '{leftParam}' debe de existir en el mapeo del ON del JOIN '{mapExprBody}' ya que el lado izquierdo es un from list que no esta nombrado"); } var mapAliases = currAliases.Select(x => new ExprRep( find: Expression.Property(onParam, x.Alias), rep: Expression.Property(onParam, x.Alias) )) .ToList(); //Encontrar el alias del left: var rightAlias = currAliases.Where(x => x.Expr == rightParam).Select(x => new ExprRep(x.Expr, Expression.Property(onParam, x.Alias))).FirstOrDefault(); if (rightAlias != null) { mapAliases.Add(rightAlias); } var currentExprAlias = new ExprAliasList(mapAliases, leftParam, onParam, null); ret.Add(currentExprAlias); var repList = currAliases.Select(x => new ExprRep( find: leftOnParam == null ? x.Expr : ReplaceVisitor.Replace(x.Expr, leftParam, leftOnParam), rep: Expression.Property(onParam, x.Alias) )) .ToList(); //Sustituir todos los subRet: var subRetSubs = subRet.Select(list => new ExprAliasList( items: list.Items.Select(item => new ExprRep( item.Find, ReplaceExprList(item.Rep, repList) )).ToList(), leftParam: list.LeftParam, currParam: list.CurrParam, leftOn: null )) .ToList() ; ret.AddRange(subRetSubs); //Agregar el alias del lado izquierdo: var leftAlias = currAliases.Where(x => x.Expr == leftParam).Select(x => new ExprRep(x.Expr, Expression.Property(onParam, x.Alias))).FirstOrDefault(); if (leftAlias != null) { var fromAlias = new ExprAliasList(new[] { leftAlias }, leftParam, onParam, null); ret.Add(fromAlias); } else { //Reemplazar el lado izquierdo de este JOIN con el ON del JOIN izquierdo leftAlias = new ExprRep(leftParam, leftOnParam); var mapAlRep = currAliases.Select(x => new ExprRep(x.Expr, ReplaceVisitor.Replace(x.Expr, leftParam, leftOnParam))).ToList(); var fromAlias = new ExprAliasList(mapAlRep, leftParam, onParam, null); ret.Add(fromAlias); } return ret; }