< Summary - Code Coverage

Information
Class: Plainquire.Filter.EntityFilter
Assembly: Plainquire.Filter
File(s): /home/runner/work/plainquire/plainquire/Plainquire.Filter/Plainquire.Filter/Filters/EntityFilter.cs
Tag: 64_13932151703
Line coverage
99%
Covered lines: 126
Uncovered lines: 1
Coverable lines: 127
Total lines: 511
Line coverage: 99.2%
Branch coverage
88%
Covered branches: 37
Total branches: 42
Branch coverage: 88%
Method coverage
95%
Covered methods: 19
Total methods: 20
Method coverage: 95%

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
.ctor()100%11100%
.ctor(...)100%11100%
Clone()100%110%
IsEmpty()50%22100%
GetPropertyFilterSyntaxInternal(...)50%22100%
GetPropertyFilterValuesInternal(...)50%22100%
GetNestedFilterInternal(...)50%22100%
GetNestedFilterInternal(...)50%22100%
AddInternal(...)100%11100%
AddNestedInternal(...)100%11100%
ReplaceInternal(...)100%11100%
ReplaceNestedInternal(...)100%11100%
RemoveInternal(...)100%11100%
ClearInternal()100%11100%
CreateFilter(...)100%66100%
GetPropertyFilters(...)100%66100%
GetNestedObjectFilters(...)100%88100%
CreateNestedListFilters(...)100%88100%
UseConditionalAccess(...)100%44100%

File(s)

/home/runner/work/plainquire/plainquire/Plainquire.Filter/Plainquire.Filter/Filters/EntityFilter.cs

#LineLine coverage
 1using Plainquire.Filter.Abstractions;
 2using Plainquire.Filter.JsonConverters;
 3using Plainquire.Filter.PropertyFilterExpressions;
 4using System;
 5using System.Collections.Generic;
 6using System.Diagnostics;
 7using System.Diagnostics.CodeAnalysis;
 8using System.Linq;
 9using System.Linq.Expressions;
 10using System.Reflection;
 11using System.Text.Json;
 12using System.Text.Json.Serialization;
 13
 14namespace Plainquire.Filter;
 15
 16/// <summary>
 17/// Hub to create filter expressions for <typeparamref name="TEntity"/> with fluent API.
 18/// </summary>
 19/// <typeparam name="TEntity">The type to be filtered.</typeparam>
 20[JsonConverter(typeof(EntityFilterConverter.Factory))]
 21[DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")]
 22public class EntityFilter<TEntity> : EntityFilter
 23{
 24    /// <summary>
 25    /// Initializes a new instance of the <see cref="EntityFilter{TEntity}"/> class.
 26    /// </summary>
 27    public EntityFilter() { }
 28
 29    /// <summary>
 30    /// Initializes a new instance of the <see cref="EntityFilter{TEntity}"/> class.
 31    /// </summary>
 32    /// <param name="configuration">The configuration to use.</param>
 33    public EntityFilter(FilterConfiguration configuration)
 34        : base(configuration) { }
 35
 36    /// <summary>
 37    /// Gets the filter syntax for the given <paramref name="property"/>.
 38    /// </summary>
 39    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 40    /// <param name="property">The property to get the filter for.</param>
 41    public string? GetPropertyFilterSyntax<TProperty>(Expression<Func<TEntity, TProperty?>> property)
 42        => GetPropertyFilterSyntaxInternal(property);
 43
 44    /// <summary>
 45    /// Get the filters applied to the given property.
 46    /// </summary>
 47    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 48    /// <param name="property">The property to get the filter for.</param>
 49    public ValueFilter[]? GetPropertyFilterValues<TProperty>(Expression<Func<TEntity, TProperty?>> property)
 50        => GetPropertyFilterValuesInternal(property);
 51
 52    /// <summary>
 53    /// Gets the <see cref="EntityFilter{TProperty}"/> for the given nested class <typeparamref name="TProperty"/>.
 54    /// </summary>
 55    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 56    /// <param name="property">The property to get the filter for.</param>
 57    public EntityFilter<TProperty>? GetNestedFilter<TProperty>(Expression<Func<TEntity, TProperty?>> property)
 58        => GetNestedFilterInternal(property);
 59
 60    /// <summary>
 61    /// Gets the <see cref="EntityFilter{TProperty}"/> for the given nested list <typeparamref name="TList"/>.
 62    /// </summary>
 63    /// <typeparam name="TList">The type of the list of <typeparamref name="TProperty"/>.</typeparam>
 64    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 65    /// <param name="property">The property to get the filter for.</param>
 66    public EntityFilter<TProperty>? GetNestedFilter<TList, TProperty>(Expression<Func<TEntity, TList?>> property)
 67        where TList : IEnumerable<TProperty>
 68        => GetNestedFilterInternal<TEntity, TList, TProperty>(property);
 69
 70    /// <summary>
 71    /// Adds a filter for the given property. Existing filters for the same property are preserved.
 72    /// </summary>
 73    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 74    /// <param name="property">The property to filter.</param>
 75    /// <param name="filters">The filters to use.</param>
 76    public EntityFilter<TEntity> Add<TProperty>(Expression<Func<TEntity, TProperty?>> property, params ValueFilter[]? fi
 77    {
 78        AddInternal(property, filters);
 79        return this;
 80    }
 81
 82    /// <summary>
 83    /// Adds a nested filter for the given property. Existing filters for the same property are preserved.
 84    /// </summary>
 85    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 86    /// <param name="property">The property to filter.</param>
 87    /// <param name="nestedFilter">The nested class filter.</param>
 88    public EntityFilter<TEntity> AddNested<TProperty>(Expression<Func<TEntity, TProperty?>> property, EntityFilter<TProp
 89    {
 90        if (nestedFilter == null)
 91            return this;
 92
 93        AddNestedInternal(property, nestedFilter);
 94        return this;
 95    }
 96
 97    /// <summary>
 98    /// Adds a nested filter for the given enumerable property. Existing filters for the same property are preserved.
 99    /// </summary>
 100    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 101    /// <typeparam name="TNested">The nested type to be filtered.</typeparam>
 102    /// <param name="property">The property to filter. Must implement <see cref="IEnumerable{T}"/>.</param>
 103    /// <param name="nestedFilter">The nested class filter.</param>
 104    public EntityFilter<TEntity> AddNested<TProperty, TNested>(Expression<Func<TEntity, TProperty?>> property, EntityFil
 105        where TProperty : IEnumerable<TNested>
 106    {
 107        if (nestedFilter == null)
 108            return this;
 109
 110        AddNestedInternal(property, nestedFilter);
 111        return this;
 112    }
 113
 114    /// <summary>
 115    /// Replaces the filter for the given property. Existing filters for the same property are removed.
 116    /// </summary>
 117    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 118    /// <param name="property">The property to filter.</param>
 119    /// <param name="filters">The filters to use.</param>
 120    public EntityFilter<TEntity> Replace<TProperty>(Expression<Func<TEntity, TProperty?>> property, params ValueFilter[]
 121    {
 122        ReplaceInternal(property, filters);
 123        return this;
 124    }
 125
 126    /// <summary>
 127    /// Replaces the nested filter for the given property. Existing filters for the same property are removed.
 128    /// </summary>
 129    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 130    /// <param name="property">The property to filter.</param>
 131    /// <param name="nestedFilter">The nested class filter.</param>
 132    public EntityFilter<TEntity> ReplaceNested<TProperty>(Expression<Func<TEntity, TProperty?>> property, EntityFilter<T
 133    {
 134        if (nestedFilter == null)
 135            return Remove(property);
 136
 137        ReplaceNestedInternal(property, nestedFilter);
 138        return this;
 139    }
 140
 141    /// <summary>
 142    /// Replaces the nested filter for the given enumerable property. Existing filters for the same property are removed
 143    /// </summary>
 144    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 145    /// <typeparam name="TNested">The nested type to be filtered.</typeparam>
 146    /// <param name="property">The property to filter. Must implement <see cref="IEnumerable{T}"/>.</param>
 147    /// <param name="nestedFilter">The nested class filter.</param>
 148    public EntityFilter<TEntity> ReplaceNested<TProperty, TNested>(Expression<Func<TEntity, TProperty?>> property, Entit
 149        where TProperty : IEnumerable<TNested>
 150    {
 151        if (nestedFilter == null)
 152            return Remove(property);
 153
 154        ReplaceNestedInternal(property, nestedFilter);
 155        return this;
 156    }
 157
 158    /// <summary>
 159    /// Remove all filters for the specified property.
 160    /// </summary>
 161    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 162    /// <param name="property">The property to remove all filters for.</param>
 163    public EntityFilter<TEntity> Remove<TProperty>(Expression<Func<TEntity, TProperty?>> property)
 164    {
 165        RemoveInternal(property);
 166        return this;
 167    }
 168
 169    /// <summary>
 170    /// Removes all filters of all properties.
 171    /// </summary>
 172    public EntityFilter<TEntity> Clear()
 173    {
 174        ClearInternal();
 175        return this;
 176    }
 177
 178    /// <summary>
 179    /// Creates a deep clone of this filter.
 180    /// </summary>
 181    public new EntityFilter<TEntity> Clone()
 182        => JsonSerializer.Deserialize<EntityFilter<TEntity>>(JsonSerializer.Serialize(this))!;
 183
 184    /// <summary>
 185    /// Casts this filter to a different type (by creating a deep clone).
 186    /// Filtered properties are matched by type (check if assignable) and name (case-sensitive).
 187    /// </summary>
 188    /// <typeparam name="TDestination">The type of the destination entity to filter.</typeparam>
 189    public EntityFilter<TDestination> Cast<TDestination>()
 190    {
 191        var castFilter = JsonSerializer.Deserialize<EntityFilter<TDestination>>(JsonSerializer.Serialize(this))!;
 192        var sourceProperties = typeof(TEntity).GetProperties();
 193        var destinationProperties = typeof(TDestination).GetProperties().ToList();
 194
 195        foreach (var sourceProperty in sourceProperties)
 196        {
 197            var sameDestinationPropertyExists = destinationProperties
 198                .Exists(x =>
 199                    x.Name.EqualsOrdinal(sourceProperty.Name) &&
 200                    x.PropertyType.IsAssignableFrom(sourceProperty.PropertyType)
 201                );
 202
 203            if (!sameDestinationPropertyExists)
 204            {
 205                castFilter.PropertyFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(sourceProperty.Name));
 206                castFilter.NestedFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(sourceProperty.Name));
 207            }
 208        }
 209
 210        return castFilter;
 211    }
 212
 213    /// <summary>
 214    /// Creates the filter expression. Returns <c>null</c> when filter is empty.
 215    /// </summary>
 216    /// <param name="interceptor">An interceptor to manipulate the generated filters.</param>
 217    /// <param name="useAsCompiledExpression">Whether the generated expression will be compiled later. Used to determine
 218    public Expression<Func<TEntity, bool>>? CreateFilter(IFilterInterceptor? interceptor = null, bool useAsCompiledExpre
 219        => CreateFilter<TEntity>(interceptor, useAsCompiledExpression);
 220
 221    /// <summary>
 222    /// Performs an implicit conversion from <see cref="EntityFilter{TEntity}"/> to <see cref="Expression{TDelegate}"/> 
 223    /// </summary>
 224    /// <param name="filter">The filter to convert.</param>
 225    public static implicit operator Expression<Func<TEntity, bool>>(EntityFilter<TEntity> filter)
 226        => filter.CreateFilter(useAsCompiledExpression: false) ?? (x => true);
 227
 228    /// <summary>
 229    /// Performs an implicit conversion from <see cref="EntityFilter{TEntity}"/> to <see cref="Func{T, TResult}"/>.
 230    /// </summary>
 231    /// <param name="filter">The filter to convert.</param>
 232    public static implicit operator Func<TEntity, bool>(EntityFilter<TEntity> filter)
 233        => (filter.CreateFilter(useAsCompiledExpression: true) ?? (x => true)).Compile();
 234
 235    /// <inheritdoc />
 236    public override string ToString()
 237        => CreateFilter()?.ToString() ?? string.Empty;
 238
 239    [ExcludeFromCodeCoverage]
 240    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
 241    private string DebuggerDisplay => CreateFilter()?.ToString() ?? "<EMPTY>";
 242}
 243
 244/// <inheritdoc cref="EntityFilter{TEntity}" />
 245[JsonConverter(typeof(EntityFilterConverter))]
 246public class EntityFilter : ICloneable
 247{
 1248    private static readonly MethodInfo _createFilterMethod = typeof(EntityFilter).GetMethods(BindingFlags.Instance | Bin
 249
 250    internal List<PropertyFilter> PropertyFilters;
 251    internal List<NestedFilter> NestedFilters;
 252
 253    /// <summary>
 254    /// Gets or sets the default configuration. Can be used to set a system-wide configuration.
 255    /// </summary>
 256    public FilterConfiguration? Configuration { get; internal set; }
 257
 258    /// <summary>
 259    /// Initializes a new instance of the <see cref="EntityFilter"/> class.
 260    /// </summary>
 261    public EntityFilter()
 262    {
 16883263        PropertyFilters = [];
 16883264        NestedFilters = [];
 16883265    }
 266
 267    /// <summary>
 268    /// Initializes a new instance of the <see cref="EntityFilter"/> class.
 269    /// </summary>
 270    /// <param name="configuration">The configuration to use.</param>
 271    public EntityFilter(FilterConfiguration configuration)
 9935272        : this()
 9935273        => Configuration = configuration;
 274
 275    /// <inheritdoc />
 276    public object Clone()
 0277        => JsonSerializer.Deserialize<EntityFilter>(JsonSerializer.Serialize(this))!;
 278
 279    /// <summary>
 280    /// Indicates whether this filter is empty.
 281    /// </summary>
 2282    public bool IsEmpty() => !PropertyFilters.Any() && !NestedFilters.Any();
 283
 284    /// <summary>
 285    /// Gets the filter syntax for the given <typeparamref name="TProperty"/>.
 286    /// </summary>
 287    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 288    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 289    /// <param name="property">The property to get the filter for.</param>
 290    protected string? GetPropertyFilterSyntaxInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>> property
 291    {
 2292        var propertyName = property.GetPropertyName();
 2293        var propertyFilter = PropertyFilters.FirstOrDefault(x => x.PropertyName.EqualsOrdinal(propertyName));
 2294        return ValueFilterExtensions.ToString(propertyFilter?.ValueFilters);
 295    }
 296
 297    /// <summary>
 298    /// Get the filters applied to the given property.
 299    /// </summary>
 300    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 301    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 302    /// <param name="property">The property to get the filter for.</param>
 303    protected ValueFilter[]? GetPropertyFilterValuesInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>> p
 304    {
 2305        var propertyName = property.GetPropertyName();
 2306        return PropertyFilters.FirstOrDefault(predicate => predicate.PropertyName.EqualsOrdinal(propertyName))?.ValueFil
 307    }
 308
 309    /// <summary>
 310    /// Gets the <see cref="EntityFilter{TProperty}"/> for the given nested class <typeparamref name="TProperty"/>.
 311    /// </summary>
 312    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 313    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 314    /// <param name="property">The property to get the filter for.</param>
 315    protected EntityFilter<TProperty>? GetNestedFilterInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>>
 316    {
 1317        var propertyName = property.GetPropertyName();
 1318        return (EntityFilter<TProperty>?)NestedFilters.FirstOrDefault(x => x.PropertyName.EqualsOrdinal(propertyName))?.
 319    }
 320
 321    /// <summary>
 322    /// Gets the <see cref="EntityFilter{TProperty}"/> for the given nested list <typeparamref name="TList"/>.
 323    /// </summary>
 324    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 325    /// <typeparam name="TList">The type of the list of <typeparamref name="TProperty"/>.</typeparam>
 326    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 327    /// <param name="property">The property to get the filter for.</param>
 328    protected EntityFilter<TProperty>? GetNestedFilterInternal<TEntity, TList, TProperty>(Expression<Func<TEntity, TList
 329        where TList : IEnumerable<TProperty>
 330    {
 1331        var propertyName = property.GetPropertyName();
 1332        return (EntityFilter<TProperty>?)NestedFilters.FirstOrDefault(x => x.PropertyName.EqualsOrdinal(propertyName))?.
 333    }
 334
 335    /// <summary>
 336    /// Adds a filter for the given property. Existing filters for the same property are preserved.
 337    /// </summary>
 338    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 339    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 340    /// <param name="property">The property to filter.</param>
 341    /// <param name="valueFilters">The filters to use.</param>
 342    protected void AddInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>> property, ValueFilter[]? valueF
 343    {
 108344        var propertyName = property.GetPropertyName();
 107345        PropertyFilters.Add(new PropertyFilter(propertyName, valueFilters));
 107346    }
 347
 348    /// <summary>
 349    /// Adds a nested filter for the given property. Existing filters for the same property are preserved.
 350    /// </summary>
 351    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 352    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 353    /// <typeparam name="TNested">The nested type to be filtered.</typeparam>
 354    /// <param name="property">The property to filter.</param>
 355    /// <param name="nestedFilter">The nested class filter.</param>
 356    protected void AddNestedInternal<TEntity, TProperty, TNested>(Expression<Func<TEntity, TProperty?>> property, Entity
 357    {
 2358        var propertyName = property.GetPropertyName();
 2359        NestedFilters.Add(new NestedFilter(propertyName, nestedFilter));
 2360    }
 361
 362    /// <summary>
 363    /// Replaces the filter for the given property using the default filter operator. Existing filters for the same prop
 364    /// </summary>
 365    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 366    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 367    /// <param name="property">The property to filter.</param>
 368    /// <param name="valueFilters">The filters to use.</param>
 369    protected void ReplaceInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>> property, ValueFilter[]? va
 370    {
 10057371        var propertyName = property.GetPropertyName();
 10057372        PropertyFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(propertyName));
 10057373        PropertyFilters.Add(new PropertyFilter(propertyName, valueFilters));
 10057374    }
 375
 376    /// <summary>
 377    /// Replaces the nested filter for the given property. Existing filters for the same property are removed.
 378    /// </summary>
 379    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 380    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 381    /// <typeparam name="TNested">The nested type to be filtered.</typeparam>
 382    /// <param name="property">The property to filter.</param>
 383    /// <param name="nestedFilter">The nested class filter.</param>
 384    protected void ReplaceNestedInternal<TEntity, TProperty, TNested>(Expression<Func<TEntity, TProperty?>> property, En
 385    {
 28386        var propertyName = property.GetPropertyName();
 28387        NestedFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(propertyName));
 28388        NestedFilters.Add(new NestedFilter(propertyName, nestedFilter));
 28389    }
 390
 391    /// <inheritdoc cref="EntityFilter{TEntity}.Remove{TProperty}(Expression{Func{TEntity, TProperty}})" />
 392    protected void RemoveInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>> property)
 393    {
 11394        var propertyName = property.GetPropertyName();
 11395        PropertyFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(propertyName));
 11396    }
 397
 398    /// <inheritdoc cref="EntityFilter{TEntity}.Clear" />
 399    protected void ClearInternal()
 1400        => PropertyFilters.Clear();
 401
 402    /// <inheritdoc cref="EntityFilter{TEntity}.CreateFilter" />
 403    protected internal Expression<Func<TEntity, bool>>? CreateFilter<TEntity>(IFilterInterceptor? interceptor, bool useA
 404    {
 18196405        var configuration = Configuration ?? FilterConfiguration.Default ?? new FilterConfiguration();
 18196406        interceptor ??= IFilterInterceptor.Default;
 407
 18196408        var properties = typeof(TEntity).GetProperties();
 18196409        var useConditionalAccess = UseConditionalAccess(configuration, useAsCompiledExpression);
 410
 18196411        var propertyFilters = GetPropertyFilters<TEntity>(properties, interceptor, configuration);
 16316412        var nestedObjectFilters = GetNestedObjectFilters<TEntity>(properties, useConditionalAccess, interceptor, useAsCo
 16316413        var nestedListsFilters = CreateNestedListFilters<TEntity>(properties, useConditionalAccess, interceptor, useAsCo
 414
 16316415        return propertyFilters
 16316416            .Concat(nestedObjectFilters)
 16316417            .Concat(nestedListsFilters)
 16316418            .CombineWithConditionalAnd();
 419    }
 420
 421    private List<Expression<Func<TEntity, bool>>?> GetPropertyFilters<TEntity>(PropertyInfo[] properties, IFilterInterce
 18196422        => properties
 18196423            .Reverse()
 18196424            .Join(
 18196425                PropertyFilters,
 18196426                x => x.Name,
 18196427                x => x.PropertyName,
 18196428                (propertyInfo, propertyFilter) => new { Property = propertyInfo, propertyFilter.ValueFilters },
 18196429                StringComparer.Ordinal
 18196430            )
 18196431            .Select(x =>
 18196432                interceptor?.CreatePropertyFilter<TEntity>(x.Property, x.ValueFilters, configuration)
 18196433                ?? PropertyFilterExpression.CreateFilter<TEntity>(x.Property, x.ValueFilters, configuration, interceptor
 18196434            )
 18196435            .ToList();
 436
 437    private List<Expression<Func<TEntity, bool>>?> GetNestedObjectFilters<TEntity>(PropertyInfo[] properties, bool useCo
 438    {
 16316439        return properties
 16316440            .Reverse()
 16316441            .Where(x => !x.PropertyType.IsGenericIEnumerable())
 16316442            .Join(
 16316443                NestedFilters,
 16316444                x => x.Name,
 16316445                x => x.PropertyName,
 16316446                (propertyInfo, nestedFilter) => new { Property = propertyInfo, nestedFilter.EntityFilter },
 16316447                StringComparer.Ordinal
 16316448            )
 16316449            .Select(x =>
 16316450            {
 16316451                var createFilterExpression = _createFilterMethod.MakeGenericMethod(x.Property.PropertyType);
 16316452                var nestedFilterExpression = (LambdaExpression?)createFilterExpression.Invoke(x.EntityFilter, [intercept
 16316453                if (nestedFilterExpression == null)
 16316454                    return null;
 16316455
 16316456                var propertySelector = typeof(TEntity).CreatePropertySelector(x.Property.Name);
 16316457                var propertyMatchesNested = (Expression<Func<TEntity, bool>>)nestedFilterExpression.ReplaceParameter(pro
 16316458
 16316459                if (!useConditionalAccess)
 16316460                    return propertyMatchesNested;
 16316461
 16316462                var propertyIsNotNull = propertySelector.IsNotNull(x.Property.PropertyType);
 16316463                var propertyIsNotNullLambda = propertySelector.CreateLambda<TEntity, bool>(propertyIsNotNull);
 16316464
 16316465                var filterExpression = new[] { propertyIsNotNullLambda, propertyMatchesNested }.CombineWithConditionalAn
 16316466                return filterExpression;
 16316467            })
 16316468            .ToList();
 469    }
 470
 471    private List<Expression<Func<TEntity, bool>>?> CreateNestedListFilters<TEntity>(PropertyInfo[] properties, bool useC
 16316472        => properties
 16316473            .Reverse()
 16316474            .Where(x => x.PropertyType.IsGenericIEnumerable())
 16316475            .Join(
 16316476                NestedFilters,
 16316477                x => x.Name,
 16316478                x => x.PropertyName,
 16316479                (propertyInfo, nestedFilter) => new { Property = propertyInfo, nestedFilter.EntityFilter },
 16316480                StringComparer.Ordinal
 16316481            )
 16316482            .Select(x =>
 16316483            {
 16316484                var propertyType = x.Property.PropertyType.GetGenericArguments()[0];
 16316485                var createFilterExpression = _createFilterMethod.MakeGenericMethod(propertyType);
 16316486                var nestedFilterExpression = (LambdaExpression?)createFilterExpression.Invoke(x.EntityFilter, [intercept
 16316487                if (nestedFilterExpression == null)
 16316488                    return null;
 16316489
 16316490                var propertySelector = typeof(TEntity).CreatePropertySelector(x.Property.Name);
 16316491                var propertyHasAnyNested = (Expression<Func<TEntity, bool>>)propertySelector.EnumerableAny(propertyType,
 16316492
 16316493                if (!useConditionalAccess)
 16316494                    return propertyHasAnyNested;
 16316495
 16316496                var propertyIsNotNull = propertySelector.IsNotNull(x.Property.PropertyType);
 16316497                var propertyIsNotNullLambda = propertySelector.CreateLambda<TEntity, bool>(propertyIsNotNull);
 16316498
 16316499                var filterExpression = new[] { propertyIsNotNullLambda, propertyHasAnyNested }.CombineWithConditionalAnd
 16316500                return filterExpression;
 16316501            })
 16316502            .ToList();
 503
 504    private static bool UseConditionalAccess(FilterConfiguration configuration, bool usedAsCompiledExpression)
 18196505        => configuration.UseConditionalAccess switch
 18196506        {
 2507            FilterConditionalAccess.Always => true,
 2508            FilterConditionalAccess.Never => false,
 18192509            _ => usedAsCompiledExpression
 18196510        };
 511}

Methods/Properties

.cctor()
.ctor()
.ctor(Plainquire.Filter.Abstractions.FilterConfiguration)
Clone()
IsEmpty()
GetPropertyFilterSyntaxInternal(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>)
GetPropertyFilterValuesInternal(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>)
GetNestedFilterInternal(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>)
GetNestedFilterInternal(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TList>>)
AddInternal(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,Plainquire.Filter.ValueFilter[])
AddNestedInternal(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,Plainquire.Filter.EntityFilter`1<TNested>)
ReplaceInternal(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,Plainquire.Filter.ValueFilter[])
ReplaceNestedInternal(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>,Plainquire.Filter.EntityFilter`1<TNested>)
RemoveInternal(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>)
ClearInternal()
CreateFilter(Plainquire.Filter.IFilterInterceptor,System.Boolean)
GetPropertyFilters(System.Reflection.PropertyInfo[],Plainquire.Filter.IFilterInterceptor,Plainquire.Filter.Abstractions.FilterConfiguration)
GetNestedObjectFilters(System.Reflection.PropertyInfo[],System.Boolean,Plainquire.Filter.IFilterInterceptor,System.Boolean)
CreateNestedListFilters(System.Reflection.PropertyInfo[],System.Boolean,Plainquire.Filter.IFilterInterceptor,System.Boolean)
UseConditionalAccess(Plainquire.Filter.Abstractions.FilterConfiguration,System.Boolean)