< 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: 66_15485642072
Line coverage
98%
Covered lines: 126
Uncovered lines: 2
Coverable lines: 128
Total lines: 528
Line coverage: 98.4%
Branch coverage
84%
Covered branches: 39
Total branches: 46
Branch coverage: 84.7%
Method coverage
90%
Covered methods: 19
Total methods: 21
Method coverage: 90.4%

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%11100%
.ctor()100%11100%
.ctor(...)100%11100%
Clone()100%110%
IsEmpty()100%22100%
IsEmpty(...)0%440%
GetPropertyFilterSyntaxInternal(...)50%22100%
GetPropertyFilterValuesInternal(...)100%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    /// Indicates whether the filter for the specified property is empty.
 38    /// </summary>
 39    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 40    /// <param name="property">The property to get the information for.</param>
 41    public bool IsEmpty<TProperty>(Expression<Func<TEntity, TProperty?>> property)
 42        => GetPropertyFilterValuesInternal(property)?.All(value => value.IsEmpty) ?? true;
 43
 44    /// <summary>
 45    /// Gets the filter syntax for the given <paramref name="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 string? GetPropertyFilterSyntax<TProperty>(Expression<Func<TEntity, TProperty?>> property)
 50        => GetPropertyFilterSyntaxInternal(property);
 51
 52    /// <summary>
 53    /// Get the filters applied to the given property.
 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 ValueFilter[]? GetPropertyFilterValues<TProperty>(Expression<Func<TEntity, TProperty?>> property)
 58        => GetPropertyFilterValuesInternal(property);
 59
 60    /// <summary>
 61    /// Gets the <see cref="EntityFilter{TProperty}"/> for the given nested class <typeparamref name="TProperty"/>.
 62    /// </summary>
 63    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 64    /// <param name="property">The property to get the filter for.</param>
 65    public EntityFilter<TProperty>? GetNestedFilter<TProperty>(Expression<Func<TEntity, TProperty?>> property)
 66        => GetNestedFilterInternal(property);
 67
 68    /// <summary>
 69    /// Gets the <see cref="EntityFilter{TProperty}"/> for the given nested list <typeparamref name="TList"/>.
 70    /// </summary>
 71    /// <typeparam name="TList">The type of the list of <typeparamref name="TProperty"/>.</typeparam>
 72    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 73    /// <param name="property">The property to get the filter for.</param>
 74    public EntityFilter<TProperty>? GetNestedFilter<TList, TProperty>(Expression<Func<TEntity, TList?>> property)
 75        where TList : IEnumerable<TProperty>
 76        => GetNestedFilterInternal<TEntity, TList, TProperty>(property);
 77
 78    /// <summary>
 79    /// Adds a filter for the given property. Existing filters for the same property are preserved.
 80    /// </summary>
 81    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 82    /// <param name="property">The property to filter.</param>
 83    /// <param name="filters">The filters to use.</param>
 84    public EntityFilter<TEntity> Add<TProperty>(Expression<Func<TEntity, TProperty?>> property, params ValueFilter[]? fi
 85    {
 86        AddInternal(property, filters);
 87        return this;
 88    }
 89
 90    /// <summary>
 91    /// Adds a nested filter for the given property. Existing filters for the same property are preserved.
 92    /// </summary>
 93    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 94    /// <param name="property">The property to filter.</param>
 95    /// <param name="nestedFilter">The nested class filter.</param>
 96    public EntityFilter<TEntity> AddNested<TProperty>(Expression<Func<TEntity, TProperty?>> property, EntityFilter<TProp
 97    {
 98        if (nestedFilter == null)
 99            return this;
 100
 101        AddNestedInternal(property, nestedFilter);
 102        return this;
 103    }
 104
 105    /// <summary>
 106    /// Adds a nested filter for the given enumerable property. Existing filters for the same property are preserved.
 107    /// </summary>
 108    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 109    /// <typeparam name="TNested">The nested type to be filtered.</typeparam>
 110    /// <param name="property">The property to filter. Must implement <see cref="IEnumerable{T}"/>.</param>
 111    /// <param name="nestedFilter">The nested class filter.</param>
 112    public EntityFilter<TEntity> AddNested<TProperty, TNested>(Expression<Func<TEntity, TProperty?>> property, EntityFil
 113        where TProperty : IEnumerable<TNested>
 114    {
 115        if (nestedFilter == null)
 116            return this;
 117
 118        AddNestedInternal(property, nestedFilter);
 119        return this;
 120    }
 121
 122    /// <summary>
 123    /// Replaces the filter for the given property. Existing filters for the same property are removed.
 124    /// </summary>
 125    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 126    /// <param name="property">The property to filter.</param>
 127    /// <param name="filters">The filters to use.</param>
 128    public EntityFilter<TEntity> Replace<TProperty>(Expression<Func<TEntity, TProperty?>> property, params ValueFilter[]
 129    {
 130        ReplaceInternal(property, filters);
 131        return this;
 132    }
 133
 134    /// <summary>
 135    /// Replaces the nested filter for the given property. Existing filters for the same property are removed.
 136    /// </summary>
 137    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 138    /// <param name="property">The property to filter.</param>
 139    /// <param name="nestedFilter">The nested class filter.</param>
 140    public EntityFilter<TEntity> ReplaceNested<TProperty>(Expression<Func<TEntity, TProperty?>> property, EntityFilter<T
 141    {
 142        if (nestedFilter == null)
 143            return Remove(property);
 144
 145        ReplaceNestedInternal(property, nestedFilter);
 146        return this;
 147    }
 148
 149    /// <summary>
 150    /// Replaces the nested filter for the given enumerable property. Existing filters for the same property are removed
 151    /// </summary>
 152    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 153    /// <typeparam name="TNested">The nested type to be filtered.</typeparam>
 154    /// <param name="property">The property to filter. Must implement <see cref="IEnumerable{T}"/>.</param>
 155    /// <param name="nestedFilter">The nested class filter.</param>
 156    public EntityFilter<TEntity> ReplaceNested<TProperty, TNested>(Expression<Func<TEntity, TProperty?>> property, Entit
 157        where TProperty : IEnumerable<TNested>
 158    {
 159        if (nestedFilter == null)
 160            return Remove(property);
 161
 162        ReplaceNestedInternal(property, nestedFilter);
 163        return this;
 164    }
 165
 166    /// <summary>
 167    /// Remove all filters for the specified property.
 168    /// </summary>
 169    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 170    /// <param name="property">The property to remove all filters for.</param>
 171    public EntityFilter<TEntity> Remove<TProperty>(Expression<Func<TEntity, TProperty?>> property)
 172    {
 173        RemoveInternal(property);
 174        return this;
 175    }
 176
 177    /// <summary>
 178    /// Removes all filters of all properties.
 179    /// </summary>
 180    public EntityFilter<TEntity> Clear()
 181    {
 182        ClearInternal();
 183        return this;
 184    }
 185
 186    /// <summary>
 187    /// Creates a deep clone of this filter.
 188    /// </summary>
 189    public new EntityFilter<TEntity> Clone()
 190        => JsonSerializer.Deserialize<EntityFilter<TEntity>>(JsonSerializer.Serialize(this))!;
 191
 192    /// <summary>
 193    /// Casts this filter to a different type (by creating a deep clone).
 194    /// Filtered properties are matched by type (check if assignable) and name (case-sensitive).
 195    /// </summary>
 196    /// <typeparam name="TDestination">The type of the destination entity to filter.</typeparam>
 197    public EntityFilter<TDestination> Cast<TDestination>()
 198    {
 199        var castFilter = JsonSerializer.Deserialize<EntityFilter<TDestination>>(JsonSerializer.Serialize(this))!;
 200        var sourceProperties = typeof(TEntity).GetProperties();
 201        var destinationProperties = typeof(TDestination).GetProperties().ToList();
 202
 203        foreach (var sourceProperty in sourceProperties)
 204        {
 205            var sameDestinationPropertyExists = destinationProperties
 206                .Exists(x =>
 207                    x.Name.EqualsOrdinal(sourceProperty.Name) &&
 208                    x.PropertyType.IsAssignableFrom(sourceProperty.PropertyType)
 209                );
 210
 211            if (!sameDestinationPropertyExists)
 212            {
 213                castFilter.PropertyFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(sourceProperty.Name));
 214                castFilter.NestedFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(sourceProperty.Name));
 215            }
 216        }
 217
 218        return castFilter;
 219    }
 220
 221    /// <summary>
 222    /// Creates the filter expression. Returns <c>null</c> when filter is empty.
 223    /// </summary>
 224    /// <param name="interceptor">An interceptor to manipulate the generated filters.</param>
 225    /// <param name="useAsCompiledExpression">Whether the generated expression will be compiled later. Used to determine
 226    public Expression<Func<TEntity, bool>>? CreateFilter(IFilterInterceptor? interceptor = null, bool useAsCompiledExpre
 227        => CreateFilter<TEntity>(interceptor, useAsCompiledExpression);
 228
 229    /// <summary>
 230    /// Performs an implicit conversion from <see cref="EntityFilter{TEntity}"/> to <see cref="Expression{TDelegate}"/> 
 231    /// </summary>
 232    /// <param name="filter">The filter to convert.</param>
 233    public static implicit operator Expression<Func<TEntity, bool>>(EntityFilter<TEntity> filter)
 234        => filter.CreateFilter(useAsCompiledExpression: false) ?? (x => true);
 235
 236    /// <summary>
 237    /// Performs an implicit conversion from <see cref="EntityFilter{TEntity}"/> to <see cref="Func{T, TResult}"/>.
 238    /// </summary>
 239    /// <param name="filter">The filter to convert.</param>
 240    public static implicit operator Func<TEntity, bool>(EntityFilter<TEntity> filter)
 241        => (filter.CreateFilter(useAsCompiledExpression: true) ?? (x => true)).Compile();
 242
 243    /// <inheritdoc />
 244    public override string ToString()
 245        => CreateFilter()?.ToString() ?? string.Empty;
 246
 247    [ExcludeFromCodeCoverage]
 248    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
 249    private string DebuggerDisplay => CreateFilter()?.ToString() ?? "<EMPTY>";
 250}
 251
 252/// <inheritdoc cref="EntityFilter{TEntity}" />
 253[JsonConverter(typeof(EntityFilterConverter))]
 254public class EntityFilter : ICloneable
 255{
 1256    private static readonly MethodInfo _createFilterMethod = typeof(EntityFilter).GetMethods(BindingFlags.Instance | Bin
 257
 258    internal List<PropertyFilter> PropertyFilters;
 259    internal List<NestedFilter> NestedFilters;
 260
 261    /// <summary>
 262    /// Gets or sets the default configuration. Can be used to set a system-wide configuration.
 263    /// </summary>
 264    public FilterConfiguration? Configuration { get; internal set; }
 265
 266    /// <summary>
 267    /// Initializes a new instance of the <see cref="EntityFilter"/> class.
 268    /// </summary>
 269    public EntityFilter()
 270    {
 16889271        PropertyFilters = [];
 16889272        NestedFilters = [];
 16889273    }
 274
 275    /// <summary>
 276    /// Initializes a new instance of the <see cref="EntityFilter"/> class.
 277    /// </summary>
 278    /// <param name="configuration">The configuration to use.</param>
 279    public EntityFilter(FilterConfiguration configuration)
 9935280        : this()
 9935281        => Configuration = configuration;
 282
 283    /// <inheritdoc />
 284    public object Clone()
 0285        => JsonSerializer.Deserialize<EntityFilter>(JsonSerializer.Serialize(this))!;
 286
 287    /// <summary>
 288    /// Indicates whether this filter is empty.
 289    /// </summary>
 4290    public bool IsEmpty() => !PropertyFilters.Any() && !NestedFilters.Any();
 291
 292    /// <summary>
 293    /// Indicates whether the specified property filter is empty.
 294    /// </summary>
 295    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 296    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 297    /// <param name="property">The property to get the information for.</param>
 298    public bool IsEmpty<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>> property)
 0299        => GetPropertyFilterValuesInternal(property)?.All(value => value.IsEmpty) ?? true;
 300
 301    /// <summary>
 302    /// Gets the filter syntax for the given <typeparamref name="TProperty"/>.
 303    /// </summary>
 304    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 305    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 306    /// <param name="property">The property to get the filter for.</param>
 307    protected string? GetPropertyFilterSyntaxInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>> property
 308    {
 2309        var propertyName = property.GetPropertyName();
 2310        var propertyFilter = PropertyFilters.FirstOrDefault(x => x.PropertyName.EqualsOrdinal(propertyName));
 2311        return ValueFilterExtensions.ToString(propertyFilter?.ValueFilters);
 312    }
 313
 314    /// <summary>
 315    /// Get the filters applied to the given property.
 316    /// </summary>
 317    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 318    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 319    /// <param name="property">The property to get the filter for.</param>
 320    protected ValueFilter[]? GetPropertyFilterValuesInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>> p
 321    {
 7322        var propertyName = property.GetPropertyName();
 7323        return PropertyFilters.FirstOrDefault(predicate => predicate.PropertyName.EqualsOrdinal(propertyName))?.ValueFil
 324    }
 325
 326    /// <summary>
 327    /// Gets the <see cref="EntityFilter{TProperty}"/> for the given nested class <typeparamref name="TProperty"/>.
 328    /// </summary>
 329    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 330    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 331    /// <param name="property">The property to get the filter for.</param>
 332    protected EntityFilter<TProperty>? GetNestedFilterInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>>
 333    {
 1334        var propertyName = property.GetPropertyName();
 1335        return (EntityFilter<TProperty>?)NestedFilters.FirstOrDefault(x => x.PropertyName.EqualsOrdinal(propertyName))?.
 336    }
 337
 338    /// <summary>
 339    /// Gets the <see cref="EntityFilter{TProperty}"/> for the given nested list <typeparamref name="TList"/>.
 340    /// </summary>
 341    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 342    /// <typeparam name="TList">The type of the list of <typeparamref name="TProperty"/>.</typeparam>
 343    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 344    /// <param name="property">The property to get the filter for.</param>
 345    protected EntityFilter<TProperty>? GetNestedFilterInternal<TEntity, TList, TProperty>(Expression<Func<TEntity, TList
 346        where TList : IEnumerable<TProperty>
 347    {
 1348        var propertyName = property.GetPropertyName();
 1349        return (EntityFilter<TProperty>?)NestedFilters.FirstOrDefault(x => x.PropertyName.EqualsOrdinal(propertyName))?.
 350    }
 351
 352    /// <summary>
 353    /// Adds a filter for the given property. Existing filters for the same property are preserved.
 354    /// </summary>
 355    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 356    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 357    /// <param name="property">The property to filter.</param>
 358    /// <param name="valueFilters">The filters to use.</param>
 359    protected void AddInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>> property, ValueFilter[]? valueF
 360    {
 114361        var propertyName = property.GetPropertyName();
 113362        PropertyFilters.Add(new PropertyFilter(propertyName, valueFilters));
 113363    }
 364
 365    /// <summary>
 366    /// Adds a nested filter for the given property. Existing filters for the same property are preserved.
 367    /// </summary>
 368    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 369    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 370    /// <typeparam name="TNested">The nested type to be filtered.</typeparam>
 371    /// <param name="property">The property to filter.</param>
 372    /// <param name="nestedFilter">The nested class filter.</param>
 373    protected void AddNestedInternal<TEntity, TProperty, TNested>(Expression<Func<TEntity, TProperty?>> property, Entity
 374    {
 3375        var propertyName = property.GetPropertyName();
 3376        NestedFilters.Add(new NestedFilter(propertyName, nestedFilter));
 3377    }
 378
 379    /// <summary>
 380    /// Replaces the filter for the given property using the default filter operator. Existing filters for the same prop
 381    /// </summary>
 382    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 383    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 384    /// <param name="property">The property to filter.</param>
 385    /// <param name="valueFilters">The filters to use.</param>
 386    protected void ReplaceInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>> property, ValueFilter[]? va
 387    {
 10057388        var propertyName = property.GetPropertyName();
 10057389        PropertyFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(propertyName));
 10057390        PropertyFilters.Add(new PropertyFilter(propertyName, valueFilters));
 10057391    }
 392
 393    /// <summary>
 394    /// Replaces the nested filter for the given property. Existing filters for the same property are removed.
 395    /// </summary>
 396    /// <typeparam name="TEntity">The type to be filtered.</typeparam>
 397    /// <typeparam name="TProperty">The type of the property to be filtered.</typeparam>
 398    /// <typeparam name="TNested">The nested type to be filtered.</typeparam>
 399    /// <param name="property">The property to filter.</param>
 400    /// <param name="nestedFilter">The nested class filter.</param>
 401    protected void ReplaceNestedInternal<TEntity, TProperty, TNested>(Expression<Func<TEntity, TProperty?>> property, En
 402    {
 28403        var propertyName = property.GetPropertyName();
 28404        NestedFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(propertyName));
 28405        NestedFilters.Add(new NestedFilter(propertyName, nestedFilter));
 28406    }
 407
 408    /// <inheritdoc cref="EntityFilter{TEntity}.Remove{TProperty}(Expression{Func{TEntity, TProperty}})" />
 409    protected void RemoveInternal<TEntity, TProperty>(Expression<Func<TEntity, TProperty?>> property)
 410    {
 11411        var propertyName = property.GetPropertyName();
 11412        PropertyFilters.RemoveAll(x => x.PropertyName.EqualsOrdinal(propertyName));
 11413    }
 414
 415    /// <inheritdoc cref="EntityFilter{TEntity}.Clear" />
 416    protected void ClearInternal()
 1417        => PropertyFilters.Clear();
 418
 419    /// <inheritdoc cref="EntityFilter{TEntity}.CreateFilter" />
 420    protected internal Expression<Func<TEntity, bool>>? CreateFilter<TEntity>(IFilterInterceptor? interceptor, bool useA
 421    {
 18196422        var configuration = Configuration ?? FilterConfiguration.Default ?? new FilterConfiguration();
 18196423        interceptor ??= IFilterInterceptor.Default;
 424
 18196425        var properties = typeof(TEntity).GetProperties();
 18196426        var useConditionalAccess = UseConditionalAccess(configuration, useAsCompiledExpression);
 427
 18196428        var propertyFilters = GetPropertyFilters<TEntity>(properties, interceptor, configuration);
 16316429        var nestedObjectFilters = GetNestedObjectFilters<TEntity>(properties, useConditionalAccess, interceptor, useAsCo
 16316430        var nestedListsFilters = CreateNestedListFilters<TEntity>(properties, useConditionalAccess, interceptor, useAsCo
 431
 16316432        return propertyFilters
 16316433            .Concat(nestedObjectFilters)
 16316434            .Concat(nestedListsFilters)
 16316435            .CombineWithConditionalAnd();
 436    }
 437
 438    private List<Expression<Func<TEntity, bool>>?> GetPropertyFilters<TEntity>(PropertyInfo[] properties, IFilterInterce
 18196439        => properties
 18196440            .Reverse()
 18196441            .Join(
 18196442                PropertyFilters,
 18196443                x => x.Name,
 18196444                x => x.PropertyName,
 18196445                (propertyInfo, propertyFilter) => new { Property = propertyInfo, propertyFilter.ValueFilters },
 18196446                StringComparer.Ordinal
 18196447            )
 18196448            .Select(x =>
 18196449                interceptor?.CreatePropertyFilter<TEntity>(x.Property, x.ValueFilters, configuration)
 18196450                ?? PropertyFilterExpression.CreateFilter<TEntity>(x.Property, x.ValueFilters, configuration, interceptor
 18196451            )
 18196452            .ToList();
 453
 454    private List<Expression<Func<TEntity, bool>>?> GetNestedObjectFilters<TEntity>(PropertyInfo[] properties, bool useCo
 455    {
 16316456        return properties
 16316457            .Reverse()
 16316458            .Where(x => !x.PropertyType.IsGenericIEnumerable())
 16316459            .Join(
 16316460                NestedFilters,
 16316461                x => x.Name,
 16316462                x => x.PropertyName,
 16316463                (propertyInfo, nestedFilter) => new { Property = propertyInfo, nestedFilter.EntityFilter },
 16316464                StringComparer.Ordinal
 16316465            )
 16316466            .Select(x =>
 16316467            {
 16316468                var createFilterExpression = _createFilterMethod.MakeGenericMethod(x.Property.PropertyType);
 16316469                var nestedFilterExpression = (LambdaExpression?)createFilterExpression.Invoke(x.EntityFilter, [intercept
 16316470                if (nestedFilterExpression == null)
 16316471                    return null;
 16316472
 16316473                var propertySelector = typeof(TEntity).CreatePropertySelector(x.Property.Name);
 16316474                var propertyMatchesNested = (Expression<Func<TEntity, bool>>)nestedFilterExpression.ReplaceParameter(pro
 16316475
 16316476                if (!useConditionalAccess)
 16316477                    return propertyMatchesNested;
 16316478
 16316479                var propertyIsNotNull = propertySelector.IsNotNull(x.Property.PropertyType);
 16316480                var propertyIsNotNullLambda = propertySelector.CreateLambda<TEntity, bool>(propertyIsNotNull);
 16316481
 16316482                var filterExpression = new[] { propertyIsNotNullLambda, propertyMatchesNested }.CombineWithConditionalAn
 16316483                return filterExpression;
 16316484            })
 16316485            .ToList();
 486    }
 487
 488    private List<Expression<Func<TEntity, bool>>?> CreateNestedListFilters<TEntity>(PropertyInfo[] properties, bool useC
 16316489        => properties
 16316490            .Reverse()
 16316491            .Where(x => x.PropertyType.IsGenericIEnumerable())
 16316492            .Join(
 16316493                NestedFilters,
 16316494                x => x.Name,
 16316495                x => x.PropertyName,
 16316496                (propertyInfo, nestedFilter) => new { Property = propertyInfo, nestedFilter.EntityFilter },
 16316497                StringComparer.Ordinal
 16316498            )
 16316499            .Select(x =>
 16316500            {
 16316501                var propertyType = x.Property.PropertyType.GetGenericArguments()[0];
 16316502                var createFilterExpression = _createFilterMethod.MakeGenericMethod(propertyType);
 16316503                var nestedFilterExpression = (LambdaExpression?)createFilterExpression.Invoke(x.EntityFilter, [intercept
 16316504                if (nestedFilterExpression == null)
 16316505                    return null;
 16316506
 16316507                var propertySelector = typeof(TEntity).CreatePropertySelector(x.Property.Name);
 16316508                var propertyHasAnyNested = (Expression<Func<TEntity, bool>>)propertySelector.EnumerableAny(propertyType,
 16316509
 16316510                if (!useConditionalAccess)
 16316511                    return propertyHasAnyNested;
 16316512
 16316513                var propertyIsNotNull = propertySelector.IsNotNull(x.Property.PropertyType);
 16316514                var propertyIsNotNullLambda = propertySelector.CreateLambda<TEntity, bool>(propertyIsNotNull);
 16316515
 16316516                var filterExpression = new[] { propertyIsNotNullLambda, propertyHasAnyNested }.CombineWithConditionalAnd
 16316517                return filterExpression;
 16316518            })
 16316519            .ToList();
 520
 521    private static bool UseConditionalAccess(FilterConfiguration configuration, bool usedAsCompiledExpression)
 18196522        => configuration.UseConditionalAccess switch
 18196523        {
 2524            FilterConditionalAccess.Always => true,
 2525            FilterConditionalAccess.Never => false,
 18192526            _ => usedAsCompiledExpression
 18196527        };
 528}

Methods/Properties

.cctor()
.ctor()
.ctor(Plainquire.Filter.Abstractions.FilterConfiguration)
Clone()
IsEmpty()
IsEmpty(System.Linq.Expressions.Expression`1<System.Func`2<TEntity,TProperty>>)
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)