< Summary - ZhEaIsNsAaBn.Libraries coverage (affected: 23 packages)

Information
Class: ZhEaIsNsAaBn.Infrastructure.Core.SoftDeleteFilterOptionsExtension
Assembly: ZhEaIsNsAaBn.Infrastructure.Core
File(s): /home/runner/work/ZhEaIsNsAaBn.Libraries.V1/ZhEaIsNsAaBn.Libraries.V1/src/DataBasePackages/ZhEaIsNsAaBn.Infrastructure.Core/Auditing/SoftDeleteFilterOptionsExtension.cs
Line coverage
90%
Covered lines: 10
Uncovered lines: 1
Coverable lines: 11
Total lines: 111
Line coverage: 90.9%
Branch coverage
100%
Covered branches: 2
Total branches: 2
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Info()100%22100%
ApplyServices(...)100%11100%
Validate(...)100%11100%
.ctor(...)100%11100%
get_IsDatabaseProvider()100%11100%
get_LogFragment()100%210%
GetServiceProviderHashCode()100%11100%
ShouldUseSameServiceProvider(...)100%11100%
PopulateDebugInfo(...)100%11100%

File(s)

/home/runner/work/ZhEaIsNsAaBn.Libraries.V1/ZhEaIsNsAaBn.Libraries.V1/src/DataBasePackages/ZhEaIsNsAaBn.Infrastructure.Core/Auditing/SoftDeleteFilterOptionsExtension.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore.Infrastructure;
 2using Microsoft.EntityFrameworkCore.Metadata;
 3using Microsoft.EntityFrameworkCore.Metadata.Builders;
 4using Microsoft.EntityFrameworkCore.Metadata.Conventions;
 5using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
 6using Microsoft.EntityFrameworkCore.Query;
 7
 8namespace ZhEaIsNsAaBn.Infrastructure.Core;
 9
 10/// <summary>
 11/// Adds a model convention to one context: every root entity type with a soft-delete flag (<c>IDeletableEntity</c> or
 12/// <c>[SoftDeleteFlag]</c>) gets the query filter <c>!flag</c>, combined with any filter the model already has.
 13/// The model is built per internal service provider, so contexts without the extension keep an unfiltered model.
 14/// </summary>
 15internal sealed class SoftDeleteFilterOptionsExtension : IDbContextOptionsExtension
 16{
 17    private DbContextOptionsExtensionInfo? _info;
 18
 19
 2420    public DbContextOptionsExtensionInfo Info => _info ??= new ExtensionInfo( this );
 21
 22
 423    public void ApplyServices( IServiceCollection services ) => new EntityFrameworkServicesBuilder( services ).TryAdd<IC
 24
 425    public void Validate( IDbContextOptions options ) { }
 26
 27
 28    private sealed class ExtensionInfo : DbContextOptionsExtensionInfo
 29    {
 830        public ExtensionInfo( IDbContextOptionsExtension extension ) : base( extension ) { }
 31
 32
 433        public override bool IsDatabaseProvider => false;
 34
 035        public override string LogFragment => "using ZhEaIsNsAaBn soft-delete filter ";
 36
 837        public override int GetServiceProviderHashCode() => 0;
 38
 439        public override bool ShouldUseSameServiceProvider( DbContextOptionsExtensionInfo other ) => other is ExtensionIn
 40
 41        public override void PopulateDebugInfo( IDictionary<string, string> debugInfo )
 42        {
 443            ArgumentNullException.ThrowIfNull( debugInfo );
 44
 445            debugInfo[ "ZhEaIsNsAaBn:SoftDeleteFilter" ] = "1";
 446        }
 47    }
 48}
 49
 50internal sealed class SoftDeleteConventionSetPlugin : IConventionSetPlugin
 51{
 52    public ConventionSet ModifyConventions( ConventionSet conventionSet )
 53    {
 54        ArgumentNullException.ThrowIfNull( conventionSet );
 55
 56        conventionSet.ModelFinalizingConventions.Add( new SoftDeleteQueryFilterConvention() );
 57
 58        return conventionSet;
 59    }
 60}
 61
 62internal sealed class SoftDeleteQueryFilterConvention : IModelFinalizingConvention
 63{
 64#if NET10_0_OR_GREATER
 65    /// The named filter (EF 10): <c>IgnoreQueryFilters( [ SoftDeleteQueryFilterConvention.FilterKey ] )</c> ignores onl
 66    internal const string FilterKey = "ZhEaIsNsAaBn.SoftDelete";
 67#endif
 68
 69
 70    public void ProcessModelFinalizing( IConventionModelBuilder modelBuilder, IConventionContext<IConventionModelBuilder
 71    {
 72        ArgumentNullException.ThrowIfNull( modelBuilder );
 73
 74        foreach ( var entityType in modelBuilder.Metadata.GetEntityTypes().Where( t => t.BaseType is null && !t.IsOwned(
 75        {
 76            if ( AuditMap.For( entityType.ClrType ).SoftDeleteFlag is not { } flag || entityType.FindProperty( flag.Name
 77
 78
 79            var entity = Expression.Parameter( entityType.ClrType, "e" );
 80            var filter = Expression.Lambda( Expression.Not( Expression.Property( entity, flag ) ), entity );
 81
 82            Apply( entityType, filter );
 83        }
 84    }
 85
 86
 87#if NET10_0_OR_GREATER
 88    private static void Apply( IConventionEntityType entityType, LambdaExpression filter )
 89    {
 90        // EF 10 doesn't mix an unnamed filter with named ones: join an existing unnamed filter, else add ours by name.
 91        if ( entityType.FindDeclaredQueryFilter( null ) is { Expression: { } existing } )
 92        {
 93            entityType.SetQueryFilter( And( existing, filter ) );
 94
 95            return;
 96        }
 97
 98        entityType.SetQueryFilter( FilterKey, filter );
 99    }
 100#else
 101    private static void Apply( IConventionEntityType entityType, LambdaExpression filter )
 102        => entityType.SetQueryFilter( entityType.GetQueryFilter() is { } existing ? And( existing, filter ) : filter );
 103#endif
 104
 105    private static LambdaExpression And( LambdaExpression existing, LambdaExpression filter )
 106    {
 107        var body = ReplacingExpressionVisitor.Replace( filter.Parameters[ 0 ], existing.Parameters[ 0 ], filter.Body );
 108
 109        return Expression.Lambda( Expression.AndAlso( existing.Body, body ), existing.Parameters );
 110    }
 111}