< Summary - Code Coverage

Information
Class: Plainquire.Filter.Abstractions.ExpressionExtensions
Assembly: Plainquire.Filter.Abstractions
File(s): /home/runner/work/plainquire/plainquire/Plainquire.Filter/Plainquire.Filter.Abstractions/Extensions/ExpressionExtensions.cs
Tag: 64_13932151703
Line coverage
97%
Covered lines: 45
Uncovered lines: 1
Coverable lines: 46
Total lines: 209
Line coverage: 97.8%
Branch coverage
75%
Covered branches: 3
Total branches: 4
Branch coverage: 75%
Method coverage
95%
Covered methods: 19
Total methods: 20
Method coverage: 95%

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
CreateLambda(...)100%11100%
CreateLambda(...)100%11100%
CombineWithConditionalAnd(...)100%11100%
CombineWithConditionalOr(...)100%11100%
ReplaceParameter(...)100%11100%
CreatePropertySelector(...)100%11100%
CreatePropertySelector(...)100%11100%
ObjectToString(...)100%11100%
Cast(...)50%22100%
StringToUpper(...)100%11100%
StringContains(...)100%11100%
StringStartsWith(...)100%11100%
StringEndsWith(...)100%11100%
IsNull(...)100%110%
IsNotNull(...)100%11100%
IsNotNull(...)100%11100%
StringIsEmpty(...)100%11100%
EnumerableAny(...)100%11100%
Combine(...)100%22100%

File(s)

/home/runner/work/plainquire/plainquire/Plainquire.Filter/Plainquire.Filter.Abstractions/Extensions/ExpressionExtensions.cs

#LineLine coverage
 1using Plainquire.Filter.Abstractions.ExpressionVisitors;
 2using System;
 3using System.Collections.Generic;
 4using System.Diagnostics.CodeAnalysis;
 5using System.Linq;
 6using System.Linq.Expressions;
 7using System.Reflection;
 8
 9namespace Plainquire.Filter.Abstractions;
 10
 11/// <summary>
 12/// Extension methods for <see cref="Expression"/>.
 13/// </summary>
 14[SuppressMessage("ReSharper", "UnusedMember.Global", Justification = "Provided as library, can be used from outside")]
 15public static class ExpressionExtensions
 16{
 217    private static readonly ConstantExpression _emptyString = Expression.Constant(string.Empty, typeof(string));
 218    private static readonly MethodInfo _stringToUpperMethodInfo = typeof(string).GetMethod(nameof(string.ToUpper), Type.
 219    private static readonly MethodInfo _stringContainsMethodInfo = typeof(string).GetMethod(nameof(string.Contains), [ty
 220    private static readonly MethodInfo _stringStartsWithMethodInfo = typeof(string).GetMethod(nameof(string.StartsWith),
 221    private static readonly MethodInfo _stringEndsWithMethodInfo = typeof(string).GetMethod(nameof(string.EndsWith), [ty
 222    private static readonly MethodInfo _enumerableAnyMethodInfo = typeof(Enumerable).GetMethods().First(method => method
 223    private static readonly MethodInfo _objectToStringMethodInfo = typeof(object).GetMethods().First(method => method.Na
 24
 25    /// <summary>
 26    /// Combines a property and an expression body to a lambda expression.
 27    /// </summary>
 28    /// <typeparam name="TEntity">The type of the class that declares <typeparamref name="TProperty"/>.</typeparam>
 29    /// <typeparam name="TProperty">The type of the property.</typeparam>
 30    /// <typeparam name="TResult">The type of the result.</typeparam>
 31    /// <param name="propertySelector">The property to use as first parameter.</param>
 32    /// <param name="expression">The expression body to use.</param>
 33    public static Expression<Func<TEntity, TResult>> CreateLambda<TEntity, TProperty, TResult>(this Expression<Func<TEnt
 1611534        => Expression.Lambda<Func<TEntity, TResult>>(expression, propertySelector.Parameters);
 35
 36    /// <summary>
 37    /// Combines a property and an expression body to a lambda expression.
 38    /// </summary>
 39    /// <typeparam name="TEntity">The type of the class that declares member from <paramref name="propertySelector"/>.</
 40    /// <typeparam name="TResult">The type of the result.</typeparam>
 41    /// <param name="propertySelector">The property to use as first parameter.</param>
 42    /// <param name="expression">The expression body to use.</param>
 43    public static Expression<Func<TEntity, TResult>> CreateLambda<TEntity, TResult>(this LambdaExpression propertySelect
 3144        => Expression.Lambda<Func<TEntity, TResult>>(expression, propertySelector.Parameters);
 45
 46    /// <summary>
 47    /// Combines multiple expression with conditional AND.
 48    /// </summary>
 49    /// <typeparam name="TSource">The type of the source.</typeparam>
 50    /// <param name="expressions">The expressions to combine.</param>
 51    public static Expression<Func<TSource, bool>>? CombineWithConditionalAnd<TSource>(this IEnumerable<Expression<Func<T
 1634752        => expressions.Combine(Expression.AndAlso);
 53
 54    /// <summary>
 55    /// Combines multiple expression with conditional OR.
 56    /// </summary>
 57    /// <typeparam name="TSource">The type of the source.</typeparam>
 58    /// <param name="expressions">The expressions to combine.</param>
 59    public static Expression<Func<TSource, bool>>? CombineWithConditionalOr<TSource>(this IEnumerable<Expression<Func<TS
 660        => expressions.Combine(Expression.OrElse);
 61
 62    /// <summary>
 63    /// Replaces the parameter of a lambda expression (<c>x => true</c> => <c>y => true</c>).
 64    /// </summary>
 65    /// <param name="origin">The origin lambda expression.</param>
 66    /// <param name="replacement">The lambda expression holding the replacement as parameter.</param>
 67    public static LambdaExpression ReplaceParameter(this LambdaExpression origin, LambdaExpression replacement)
 68    {
 1569        var body = new ExpressionReplaceVisitor(origin.Parameters[0], replacement.Body).Visit(origin.Body);
 1570        return Expression.Lambda(body, replacement.Parameters[0]);
 71    }
 72
 73    /// <summary>
 74    /// Creates typed property selector for the given type.
 75    /// </summary>
 76    /// <param name="declaringType">The Type of the declaring class/struct/record.</param>
 77    /// <param name="propertyName">The Name of the property.</param>
 78    public static Expression<Func<TEntity, TProperty>> CreatePropertySelector<TEntity, TProperty>(this Type declaringTyp
 679        => (Expression<Func<TEntity, TProperty>>)declaringType.CreatePropertySelector(propertyName);
 80
 81    /// <summary>
 82    /// Creates an untyped property selector for the given type.
 83    /// </summary>
 84    /// <param name="declaringType">The Type of the declaring class/struct/record.</param>
 85    /// <param name="propertyName">The Name of the property.</param>
 86    public static LambdaExpression CreatePropertySelector(this Type declaringType, string propertyName)
 87    {
 1822688        var propertyParameter = Expression.Parameter(declaringType, "x");
 1822689        var propertyExpression = Expression.Property(propertyParameter, propertyName);
 1822690        return Expression.Lambda(propertyExpression, propertyParameter);
 91    }
 92
 93    /// <summary>
 94    /// Applies a call to <see cref="object.ToString()"/> to the given expression.
 95    /// </summary>
 96    /// <param name="expression">The expression to add the call.</param>
 97    public static Expression ObjectToString(this Expression expression)
 151298        => Expression.Call(expression, _objectToStringMethodInfo);
 99
 100    /// <summary>
 101    /// Adds a cast to the given expression.
 102    /// </summary>
 103    /// <param name="expression">The expression to cast.</param>
 104    /// <param name="sourceType">The type of the source.</param>
 105    /// <param name="destinationType">The type to cast to.</param>
 106    public static Expression Cast(this Expression expression, Type sourceType, Type destinationType)
 1056107        => sourceType != destinationType
 1056108            ? Expression.Convert(expression, destinationType)
 1056109            : expression;
 110
 111    /// <summary>
 112    /// Applies a call to <see cref="string.ToUpper()"/> to the given expression.
 113    /// </summary>
 114    /// <param name="expression">The expression to add the call.</param>
 115    public static Expression StringToUpper(this Expression expression)
 4400116        => Expression.Call(expression, _stringToUpperMethodInfo);
 117
 118    /// <summary>
 119    /// Applies a call to <see cref="string.Contains(string)"/> to the given expression.
 120    /// </summary>
 121    /// <param name="expression">The expression to add the call.</param>
 122    /// <param name="value">The value expression to seek</param>
 123    public static Expression StringContains(this Expression expression, Expression value)
 1096124        => Expression.Call(expression, _stringContainsMethodInfo, value);
 125
 126    /// <summary>
 127    /// Applies a call to <see cref="string.StartsWith(string)"/> to the given expression.
 128    /// </summary>
 129    /// <param name="expression">The expression to add the call.</param>
 130    /// <param name="value">The value expression to seek</param>
 131    public static Expression StringStartsWith(this Expression expression, Expression value)
 1206132        => Expression.Call(expression, _stringStartsWithMethodInfo, value);
 133
 134    /// <summary>
 135    /// Applies a call to <see cref="string.EndsWith(string)"/> to the given expression.
 136    /// </summary>
 137    /// <param name="expression">The expression to add the call.</param>
 138    /// <param name="value">The value expression to seek</param>
 139    public static Expression StringEndsWith(this Expression expression, Expression value)
 768140        => Expression.Call(expression, _stringEndsWithMethodInfo, value);
 141
 142    /// <summary>
 143    /// Creates an expression indicates whether the specified property equals <c>null</c>
 144    /// </summary>
 145    /// <typeparam name="TEntity">The type of the class that declares <typeparamref name="TProperty"/>.</typeparam>
 146    /// <typeparam name="TProperty">The type of the property.</typeparam>
 147    /// <param name="property">The property to check for <c>null</c>.</param>
 148    public static Expression IsNull<TEntity, TProperty>(this Expression<Func<TEntity, TProperty>> property)
 0149        => Expression.Equal(property.Body, Expression.Constant(null, typeof(TProperty)));
 150
 151    /// <summary>
 152    /// Creates an expression indicates whether the specified property not equals <c>null</c>
 153    /// </summary>
 154    /// <typeparam name="TEntity">The type of the class that declares <typeparamref name="TProperty"/>.</typeparam>
 155    /// <typeparam name="TProperty">The type of the property.</typeparam>
 156    /// <param name="property">The property to check for <c>null</c>.</param>
 157    public static Expression IsNotNull<TEntity, TProperty>(this Expression<Func<TEntity, TProperty>> property)
 2888158        => IsNotNull(property, typeof(TProperty));
 159
 160    /// <summary>
 161    /// Creates an expression indicates whether the specified property not equals <c>null</c>
 162    /// </summary>
 163    /// <param name="property">The property to check for <c>null</c>.</param>
 164    /// <param name="propertyType">The type of the property.</param>
 165    public static Expression IsNotNull(this LambdaExpression property, Type propertyType)
 2919166        => Expression.NotEqual(property.Body, Expression.Constant(null, propertyType));
 167
 168    /// <summary>
 169    /// Creates an expression indicates whether the specified property equals <see cref="string.Empty"/>.
 170    /// </summary>
 171    /// <typeparam name="TEntity">The type of the class that declares <typeparamref name="TProperty"/>.</typeparam>
 172    /// <typeparam name="TProperty">The type of the property.</typeparam>
 173    /// <param name="property">The property to check.</param>
 174    public static Expression StringIsEmpty<TEntity, TProperty>(this Expression<Func<TEntity, TProperty>> property)
 246175        => Expression.Equal(property.Body, _emptyString);
 176
 177    /// <summary>
 178    /// Applies a call to <see cref="Enumerable.Any{TSource}(IEnumerable{TSource})"/> to the given expression.
 179    /// </summary>
 180    /// <param name="property">The property to add the call.</param>
 181    /// <param name="propertyType">The type of the property.</param>
 182    /// <param name="predicate">The nested predicate.</param>
 183    public static LambdaExpression EnumerableAny(this LambdaExpression property, Type propertyType, LambdaExpression pre
 184    {
 17185        var genericEnumerableAnyMethod = _enumerableAnyMethodInfo.MakeGenericMethod(propertyType);
 17186        var propertyContainsAnyMatchesPredicate = Expression.Call(genericEnumerableAnyMethod, property.Body, predicate);
 17187        return Expression.Lambda(propertyContainsAnyMatchesPredicate, property.Parameters);
 188    }
 189
 190    private static Expression<Func<TSource, bool>>? Combine<TSource>(this IEnumerable<Expression<Func<TSource, bool>>?> 
 191    {
 16353192        var notNullExpressions = expressions
 16353193            .WhereNotNull()
 16353194            .ToList();
 195
 16353196        if (!notNullExpressions.Any())
 220197            return null;
 198
 16133199        return notNullExpressions.Aggregate((first, second) =>
 16133200        {
 16133201            // Parameter rewrite doesn't work with 'Invoke' in EF Core 3.x anymore.
 16133202            // Use the good old ExpressionVisitor, example at https://stackoverflow.com/a/5431309/1271211
 16133203            var replacedSecond = new ExpressionParameterReplaceVisitor(second.Parameters, first.Parameters).VisitAndConv
 16133204            var conditionalExpr = fn(first.Body, replacedSecond);
 16133205            var finalExpr = Expression.Lambda<Func<TSource, bool>>(conditionalExpr, first.Parameters);
 16133206            return finalExpr;
 16133207        });
 208    }
 209}

Methods/Properties

.cctor()
CreateLambda(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,System.Linq.Expressions.Expression)
CreateLambda(System.Linq.Expressions.LambdaExpression,System.Linq.Expressions.Expression)
CombineWithConditionalAnd(System.Collections.Generic.IEnumerable`1<System.Linq.Expressions.Expression`1<System.Func`2<TSource,System.Boolean>>>)
CombineWithConditionalOr(System.Collections.Generic.IEnumerable`1<System.Linq.Expressions.Expression`1<System.Func`2<TSource,System.Boolean>>>)
ReplaceParameter(System.Linq.Expressions.LambdaExpression,System.Linq.Expressions.LambdaExpression)
CreatePropertySelector(System.Type,System.String)
CreatePropertySelector(System.Type,System.String)
ObjectToString(System.Linq.Expressions.Expression)
Cast(System.Linq.Expressions.Expression,System.Type,System.Type)
StringToUpper(System.Linq.Expressions.Expression)
StringContains(System.Linq.Expressions.Expression,System.Linq.Expressions.Expression)
StringStartsWith(System.Linq.Expressions.Expression,System.Linq.Expressions.Expression)
StringEndsWith(System.Linq.Expressions.Expression,System.Linq.Expressions.Expression)
IsNull(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>)
IsNotNull(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>)
IsNotNull(System.Linq.Expressions.LambdaExpression,System.Type)
StringIsEmpty(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>)
EnumerableAny(System.Linq.Expressions.LambdaExpression,System.Type,System.Linq.Expressions.LambdaExpression)
Combine(System.Collections.Generic.IEnumerable`1<System.Linq.Expressions.Expression`1<System.Func`2<TSource,System.Boolean>>>,System.Func`3<System.Linq.Expressions.Expression,System.Linq.Expressions.Expression,System.Linq.Expressions.BinaryExpression>)