public ActionResult Filtrar(LivroViewModel search) { Expression <Func <Livro, bool> > filtro = null; Expression <Func <Livro, bool> > filtro2; if (search.Titulo != null) { var criterio = search.Titulo.ToLower() + "%"; filtro2 = ent => (DbFunctions.Like(ent.Titulo.ToLower(), criterio)); filtro = ExpressionParameterReplacer.concatenar(filtro, filtro2); } if (search.GeneroId != null) { var criterio = search.GeneroId + "%"; filtro2 = ent => (DbFunctions.Like(ent.GeneroId.ToString(), criterio)); filtro = ExpressionParameterReplacer.concatenar(filtro, filtro2); } if (search.EditoraId != null) { var criterio = search.EditoraId + "%"; filtro2 = ent => (DbFunctions.Like(ent.EditoraId.ToString(), criterio)); filtro = ExpressionParameterReplacer.concatenar(filtro, filtro2); } var query = _livroAppService.ObterTodos().Where(filtro ?? (u => true)); return(PartialView("_Resultado", query.ToList())); }
private Func <T, bool> Compile() { Expression <Func <T, bool> > result = null; foreach (Rule rule in this.m_AndRules) { if (result == null) { result = this.BuildExpression(rule); } else { var expressionToAdd = this.BuildExpression(rule); var parameterModifiedExpression = new ExpressionParameterReplacer(expressionToAdd.Parameters, result.Parameters).Visit(expressionToAdd.Body); result = Expression.Lambda <Func <T, bool> >(Expression.AndAlso(result.Body, parameterModifiedExpression), result.Parameters); } } foreach (Rule rule in this.m_OrRules) { if (result == null) { result = this.BuildExpression(rule); } else { var expressionToAdd = this.BuildExpression(rule); var parameterModifiedExpression = new ExpressionParameterReplacer(expressionToAdd.Parameters, result.Parameters).Visit(expressionToAdd.Body); result = Expression.Lambda <Func <T, bool> >(Expression.OrElse(result.Body, parameterModifiedExpression), result.Parameters); } } return(result?.Compile()); }
/// <summary> /// Комбинирует два выржанеия по заданной логике с одинаковыми входными параметрами. /// </summary> /// <typeparam name="T">Тип параметра.</typeparam> /// <param name="firstExpression">Первое выражение.</param> /// <param name="secondExpression">Второе выражени.</param> /// <param name="combiner">Логика комбинирования выражения. Смотрите класс <see cref="Expression"/>.</param> /// <returns>Скомбинированное выражение.</returns> public static Expression <T> CombineExpressions <T>(Expression <T> firstExpression, Expression <T> secondExpression, Func <Expression, Expression, BinaryExpression> combiner) { var left = firstExpression.Body; var right = new ExpressionParameterReplacer(secondExpression.Parameters, firstExpression.Parameters).Visit(secondExpression.Body); var combined = Expression.Lambda <T>(combiner(firstExpression.Body, right), firstExpression.Parameters); return(combined); }
/// <summary> /// Chain all expressions in the list by Or expression. /// </summary> /// <exception cref="ArgumentNullException"> /// When expressions is null. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// When there is no expression in expressions. /// </exception> /// <typeparam name="T">Type of model.</typeparam> /// <param name="expressions">List of expressions to chain.</param> /// <returns>Constructed expression.</returns> public static Expression <Func <T, bool> > ConstructOrChain <T>( this IEnumerable <Expression <Func <T, bool> > > expressions) { if (expressions == null) { throw new ArgumentNullException("expressions"); } if (expressions.Count() == 0) { throw new ArgumentOutOfRangeException( "expressions", "There must be at least one expression in expressions"); } ParameterExpression parameterExp = Expression.Parameter(typeof(T), "m"); Expression orChainedExpression = null; Expression visitedExp; foreach (Expression <Func <T, bool> > exp in expressions) { // Replace paremeter in expression ExpressionParameterReplacer parameterReplacer = new ExpressionParameterReplacer(exp.Parameters.First(), parameterExp); visitedExp = parameterReplacer.Visit(exp.Body); if (orChainedExpression == null) { orChainedExpression = visitedExp; } else { orChainedExpression = Expression .OrElse(orChainedExpression, visitedExp); } } return(Expression.Lambda <Func <T, bool> >( orChainedExpression, parameterExp)); }
public override void OnMethodCallVisit(MethodCallVisitEventArgs e) { if (e == null) { throw new ArgumentNullException("e"); } // working with static extension methods with one "this" argument and with instance methods with no arguments // todo: methods with multiple arguments can be implemented too // todo: maybe better check? if ((e.MethodCall.Method.IsStatic && e.MethodCall.Arguments.Count == 1) || (!e.MethodCall.Method.IsStatic && e.MethodCall.Arguments.Count == 0)) { var expressionAttribute = (ExpandWithExpressionAttribute)e.MethodCall.Method.GetCustomAttributes(typeof(ExpandWithExpressionAttribute), false).SingleOrDefault(); if (expressionAttribute == null) { throw new InvalidOperationException(String.Format( CultureInfo.InvariantCulture, "Method '{0}' in '{1}' class has no ExpandWithExpression attribute.", e.MethodCall.Method.Name, e.MethodCall.Method.DeclaringType.Name)); } var declaringType = expressionAttribute.DeclaringType ?? e.MethodCall.Method.DeclaringType; var methodName = expressionAttribute.MethodName ?? e.MethodCall.Method.Name; var expressionMethodInfo = declaringType.GetMethod( methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy); if (expressionMethodInfo == null) { throw new ArgumentException(String.Format( CultureInfo.InvariantCulture, "Method specified in ExpandWithExpression attribute of '{0}' method in '{1}' class is not found.", e.MethodCall.Method.Name, e.MethodCall.Method.DeclaringType.Name)); } var customExpandedExpression = (LambdaExpression)expressionMethodInfo.Invoke(null, new object[] { this.Scope }); // parameterExpression is object in case of instance method or single (todo: first) argument in case of extension method var parameterExpression = e.MethodCall.Method.IsStatic ? e.MethodCall.Arguments.Single() : e.MethodCall.Object; // validating custom expanded expression if (customExpandedExpression.Parameters.Single().Type != parameterExpression.Type || customExpandedExpression.Body.Type != e.MethodCall.Type) { throw new InvalidOperationException(String.Format( CultureInfo.InvariantCulture, "Method '{0}' in '{1}' class returns invalid expression.", expressionMethodInfo.Name, expressionMethodInfo.DeclaringType.Name)); } // localize expression (replace its parameter with local object expression) var localizedCustomExpandedExpression = new ExpressionParameterReplacer( customExpandedExpression.Parameters.Single(), parameterExpression) .Visit(customExpandedExpression.Body); e.SubstituteExpression = localizedCustomExpandedExpression; } }
public void TestParameterReplacer() { Expression <Func <int, int, int> > expression = (x, y) => x + y; var newParam = Expression.Parameter(typeof(int)); //Replace Parameters //Typed Lambda var case1 = ExpressionParameterReplacer.Replace(expression, expression.Parameters[0], newParam); Assert.AreEqual(newParam, case1.Parameters[0]); Assert.AreEqual(((BinaryExpression)case1.Body).Left, newParam); //Untyped Lambda var case2 = ExpressionParameterReplacer.Replace((LambdaExpression)expression, expression.Parameters[0], newParam); Assert.AreEqual(newParam, case2.Parameters[0]); Assert.AreEqual(((BinaryExpression)case2.Body).Left, newParam); //Lambda as Expression var case3 = ExpressionParameterReplacer.Replace((Expression)expression, expression.Parameters[0], newParam); Assert.AreEqual(newParam, ((LambdaExpression)case3).Parameters[0]); Assert.AreEqual(((BinaryExpression)((LambdaExpression)case3).Body).Left, newParam); //Expression (no lambda) var case4 = ExpressionParameterReplacer.Replace(expression.Body, expression.Parameters[0], newParam); Assert.AreEqual(((BinaryExpression)case4).Left, newParam); //Replace Parameters by name //Typed Lambda var case11 = ExpressionParameterReplacer.Replace(expression, "x", newParam); Assert.AreEqual(newParam, case11.Parameters[0]); Assert.AreEqual(((BinaryExpression)case11.Body).Left, newParam); //Untyped Lambda var case12 = ExpressionParameterReplacer.Replace((LambdaExpression)expression, "x", newParam); Assert.AreEqual(newParam, case12.Parameters[0]); Assert.AreEqual(((BinaryExpression)case12.Body).Left, newParam); //Lambda as Expression var case13 = ExpressionParameterReplacer.Replace((Expression)expression, "x", newParam); Assert.AreEqual(newParam, ((LambdaExpression)case13).Parameters[0]); Assert.AreEqual(((BinaryExpression)((LambdaExpression)case13).Body).Left, newParam); //Expression (no lambda) var case14 = ExpressionParameterReplacer.Replace(expression.Body, "x", newParam); Assert.AreEqual(((BinaryExpression)case14).Left, newParam); //Replace Parameters by type, throws error because to ints //Untyped Lambda Assert.Throws <InvalidOperationException>(() => ExpressionParameterReplacer.Replace((LambdaExpression)expression, typeof(int), newParam)); //Replace Parameters by type Expression <Func <int, int> > simpleExpression = x => x; //Typed Lambda var case21 = ExpressionParameterReplacer.Replace(simpleExpression, typeof(int), newParam); Assert.AreEqual(newParam, case21.Parameters[0]); Assert.AreEqual(case21.Body, newParam); //Untyped Lambda var case22 = ExpressionParameterReplacer.Replace((LambdaExpression)simpleExpression, typeof(int), newParam); Assert.AreEqual(newParam, case22.Parameters[0]); Assert.AreEqual(case22.Body, newParam); //Lambda as Expression var case23 = ExpressionParameterReplacer.Replace((Expression)simpleExpression, typeof(int), newParam); Assert.AreEqual(newParam, ((LambdaExpression)case23).Parameters[0]); Assert.AreEqual(((LambdaExpression)case23).Body, newParam); //Expression (no lambda) var case24 = ExpressionParameterReplacer.Replace(expression.Body, typeof(int), newParam); Assert.AreEqual(((BinaryExpression)case24).Left, newParam); //Change Type by type Expression <Func <A, A, bool> > expression2 = (x, y) => x.a > 0 && y.b > 0; //Untyped Lambda var case31 = ExpressionParameterReplacer.ChangeType((LambdaExpression)expression2, typeof(A), typeof(B)); Assert.AreEqual(case31.Parameters[0].Type, typeof(B)); //Lambda as Expression var case32 = ExpressionParameterReplacer.ChangeType((Expression)expression2, typeof(A), typeof(B)); Assert.IsTrue(case32.GetParameters(x => x.Type == typeof(B)).Any()); //Expression var case32a = ExpressionParameterReplacer.ChangeType(expression2.Body, typeof(A), typeof(B)); Assert.IsTrue(case32a.GetParameters(x => x.Type == typeof(B)).Any()); //Change Type by name //Untyped Lambda var case33 = ExpressionParameterReplacer.ChangeType((LambdaExpression)expression2, "x", typeof(B)); Assert.AreEqual(case33.Parameters[0].Type, typeof(B)); //Lambda as Expression var case34 = ExpressionParameterReplacer.ChangeType((Expression)expression2, "x", typeof(B)); Assert.IsTrue(case34.GetParameters(x => x.Type == typeof(B)).Any()); //Expression var case35 = ExpressionParameterReplacer.ChangeType(expression2.Body, "x", typeof(B)); Assert.IsTrue(case35.GetParameters(x => x.Type == typeof(B)).Any()); }
/// <summary> /// Gets the select expression. /// </summary> /// <param name="sourceExpression"> /// The source expression. /// </param> /// <param name="projectionType"> /// Type of the projection. /// </param> /// <param name="projectionConfig"> /// The projection config. /// </param> /// <param name="projectionMemberMetadatas"> /// The projection member metadatas. /// </param> /// <returns> /// The select expression from source to result via projection. /// </returns> private Expression GetSelectExpression(Expression sourceExpression, Type projectionType, object projectionConfig, IEnumerable <ProjectionMemberMetadata> projectionMemberMetadatas) { // todo: performance issues var resultMemberBindings = new List <MemberBinding>(); foreach (var selectMemberMetadata in projectionMemberMetadatas) { var resultProperty = (PropertyInfo)selectMemberMetadata.Member; // for SelectExpression simply bind property to expression var expressionAttribute = selectMemberMetadata.MemberAttribute as SelectExpressionAttribute; if (expressionAttribute != null) { var declaringType = expressionAttribute.DeclaringType ?? projectionType; var methodName = expressionAttribute.MethodName ?? resultProperty.Name; var expressionMethod = declaringType.GetMethod( methodName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy); if (expressionMethod == null) { throw new ArgumentException(String.Format( CultureInfo.InvariantCulture, "Method specified in SelectExpressionMethod attribute of '{0}' property in '{1}' class is not found.", resultProperty, projectionType)); } // todo: rewrite (projectionConfig is passed inside even if DeclaringType is another, works because with static methods in another DeclaringTypes projectionConfig is not used) var customBindingExpression = (LambdaExpression)InvokeMethod(projectionConfig, expressionMethod, this.Scope); // localize expression (replace its parameter with local sourceExpression) var localizedCustomBindingExpression = new ExpressionParameterReplacer( customBindingExpression.Parameters.Single(), sourceExpression) .Visit(customBindingExpression.Body); // fixing up collection type if needed localizedCustomBindingExpression = localizedCustomBindingExpression.FixupCollectionType(resultProperty.PropertyType); resultMemberBindings.Add(Expression.Bind(resultProperty, localizedCustomBindingExpression)); } // for SelectProperty there may be variants var propertyAttribute = selectMemberMetadata.MemberAttribute as SelectPropertyAttribute; if (propertyAttribute != null) { var sourcePropertyPath = propertyAttribute.Path ?? resultProperty.Name; var sourcePropertyExpression = (Expression)sourceExpression.PropertyPath(sourcePropertyPath); var sourcePropertyElementType = TypeSystem.GetElementType(sourcePropertyExpression.Type); var resultPropertyElementType = TypeSystem.GetElementType(resultProperty.PropertyType); // note: trying to do as much as it is possible // todo: maybe do less? eg. no blind converts if (sourcePropertyExpression.Type == resultProperty.PropertyType) { // 1. simple bind if types are equal resultMemberBindings.Add(Expression.Bind(resultProperty, sourcePropertyExpression)); } else if (resultProperty.PropertyType.IsAssignableFrom(sourcePropertyExpression.Type)) { // 2. if types are assignable then assign with cast (which is crucial for eg. lift to null) // this will cover lift to nulls (int -> int?), List<int> -> IEnumerable<int>, and some other situations // but will not cover eg. some element types conversions (eg. int -> decimal) var resultPropertyBindingExpression = Expression.Convert(sourcePropertyExpression, resultProperty.PropertyType); resultMemberBindings.Add(Expression.Bind(resultProperty, resultPropertyBindingExpression)); } else if (((sourcePropertyElementType == null) && (resultPropertyElementType == null)) && (sourcePropertyExpression.Type != resultProperty.PropertyType)) { // 4-5. if both properties are not sequence types and property types are differ then it is probably an object projection or simple convert if (IsProjectionFor(resultProperty.PropertyType, sourcePropertyExpression.Type)) { // 4. object projection var resultPropertyBindingExpression = this.ApplyObjectProjection(sourcePropertyExpression, resultProperty.PropertyType, null /* note: no projectionConfig here */); resultMemberBindings.Add(Expression.Bind(resultProperty, resultPropertyBindingExpression)); } else { // 5. simple convert var resultPropertyBindingExpression = Expression.Convert(sourcePropertyExpression, resultProperty.PropertyType); resultMemberBindings.Add(Expression.Bind(resultProperty, resultPropertyBindingExpression)); } } else if (((sourcePropertyElementType != null) && (resultPropertyElementType != null)) && (sourcePropertyElementType != resultPropertyElementType)) { // 6-7. if both properties are sequence types and element types are differ then it is probably a sequence projection or sequence convert if (IsProjectionFor(resultPropertyElementType, sourcePropertyElementType)) { // 6. sequence projection var resultPropertyBindingExpression = this.ApplySequenceProjection(sourcePropertyExpression, resultPropertyElementType, null /* note: no projectionConfig here */) .FixupCollectionType(resultProperty.PropertyType); resultMemberBindings.Add(Expression.Bind(resultProperty, resultPropertyBindingExpression)); } else { // 7. sequence convert: sequence.Select(element => (type) element) var sourceElementParameter = Expression.Parameter(sourcePropertyElementType, sourcePropertyElementType.Name); var sourceElementConvertedExpression = Expression.Convert(sourceElementParameter, resultPropertyElementType); var resultPropertyBindingExpression = sourcePropertyExpression.Select(sourceElementParameter.ToLambda(sourceElementConvertedExpression)) .FixupCollectionType(resultProperty.PropertyType); resultMemberBindings.Add(Expression.Bind(resultProperty, resultPropertyBindingExpression)); } } else { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Unknown binding of projection property. Projection {0}, property {1}.", projectionType, selectMemberMetadata.Member)); } } } var resultInitExpression = Expression.MemberInit(Expression.New(projectionType), resultMemberBindings); return(resultInitExpression); }
/// <summary> /// Combines the first expression with the second /// </summary> /// <param name="left">Expression that accepts <c>TIn</c> and return <c>TInter</c></param> /// <param name="right">Expression that accepts <c>TInter</c> and return <c>TOut</c></param> /// <typeparam name="TIn">Input type</typeparam> /// <typeparam name="TInter">Intermediate type</typeparam> /// <typeparam name="TOut">Output type</typeparam> /// <returns>Expression that accepts <c>TIn</c> and return <c>TOut</c></returns> public static Expression <Func <TIn, TOut> > Compose <TIn, TInter, TOut>(this Expression <Func <TIn, TInter> > left, Expression <Func <TInter, TOut> > right) { var merged = new ExpressionParameterReplacer(right.Parameters[0], left.Body).Visit(right.Body); return(Expression.Lambda <Func <TIn, TOut> >(merged, left.Parameters[0])); }