< Summary

Information
Class: Elsa.Persistence.EFCore.Store<T1, T2>
Assembly: Elsa.Persistence.EFCore.Common
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Persistence.EFCore.Common/Store.cs
Line coverage
37%
Covered lines: 80
Uncovered lines: 131
Coverable lines: 211
Total lines: 628
Line coverage: 37.9%
Branch coverage
43%
Covered branches: 46
Total branches: 106
Branch coverage: 43.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
.cctor()100%11100%
CreateDbContextAsync()100%11100%
AddAsync()100%210%
AddAsync()100%44100%
AddManyAsync()100%210%
AddManyAsync()83.33%6687.5%
SaveAsync()100%210%
SaveAsync()83.33%171266.66%
SaveManyAsync()100%210%
SaveManyAsync()85.71%201468.42%
UpdateAsync(...)100%210%
UpdateAsync()0%2040%
UpdatePartialAsync()0%2040%
FindAsync()100%210%
FindAsync()0%4260%
FindAsync()100%210%
FindAsync()100%11100%
FindAsync()100%210%
FindAsync()100%210%
FindManyAsync()100%210%
FindManyAsync()0%4260%
FindManyAsync()100%210%
FindManyAsync()0%156120%
ListAsync(...)100%210%
ListAsync()0%4260%
DeleteAsync()0%620%
DeleteWhereAsync()100%22100%
DeleteWhereAsync()100%22100%
QueryAsync()100%11100%
QueryAsync()100%210%
QueryAsync()100%11100%
QueryAsync()100%88100%
QueryAsync()100%11100%
QueryAsync()75%4487.5%
CountAsync()100%210%
CountAsync()0%2040%
AnyAsync()100%210%
AnyAsync()0%620%
CountAsync()100%210%
CountAsync()0%2040%
CountAsync()100%210%
CountAsync()0%2040%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Persistence.EFCore.Common/Store.cs

#LineLine coverage
 1using System.Linq.Expressions;
 2using Elsa.Common.Entities;
 3using Elsa.Common.Models;
 4using Elsa.Common.Multitenancy;
 5using Elsa.Persistence.EFCore.Extensions;
 6using Elsa.Extensions;
 7using JetBrains.Annotations;
 8using Microsoft.EntityFrameworkCore;
 9using Microsoft.Extensions.DependencyInjection;
 10using Open.Linq.AsyncExtensions;
 11
 12namespace Elsa.Persistence.EFCore;
 13
 14/// <summary>
 15/// A generic repository class around EF Core for accessing entities.
 16/// </summary>
 17/// <typeparam name="TDbContext">The type of the database context.</typeparam>
 18/// <typeparam name="TEntity">The type of the entity.</typeparam>
 19[PublicAPI]
 295920public class Store<TDbContext, TEntity>(IDbContextFactory<TDbContext> dbContextFactory, IServiceProvider serviceProvider
 21{
 22    // ReSharper disable once StaticMemberInGenericType
 23    // Justification: This is a static member that is used to ensure that only one thread can access the database for TE
 324    private static readonly SemaphoreSlim Semaphore = new(1, 1);
 25
 26    /// <summary>
 27    /// Creates a new instance of the database context.
 28    /// </summary>
 29    /// <param name="cancellationToken">The cancellation token.</param>
 30    /// <returns>The database context.</returns>
 599331    public async Task<TDbContext> CreateDbContextAsync(CancellationToken cancellationToken = default) => await dbContext
 32
 33    /// <summary>
 34    /// Adds the specified entity.
 35    /// </summary>
 36    /// <param name="entity">The entity to add.</param>
 37    /// <param name="cancellationToken">The cancellation token.</param>
 38    public async Task AddAsync(TEntity entity, CancellationToken cancellationToken = default)
 39    {
 040        await AddAsync(entity, null, cancellationToken);
 041    }
 42
 43    /// <summary>
 44    /// Adds the specified entity.
 45    /// </summary>
 46    /// <param name="entity">The entity to add.</param>
 47    /// <param name="onAdding">The callback to invoke before adding the entity.</param>
 48    /// <param name="cancellationToken">The cancellation token.</param>
 49    public async Task AddAsync(TEntity entity, Func<TDbContext, TEntity, CancellationToken, ValueTask>? onAdding, Cancel
 50    {
 4851        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 52
 4853        if (onAdding != null)
 4854            await onAdding(dbContext, entity, cancellationToken);
 55
 4856        var set = dbContext.Set<TEntity>();
 4857        await set.AddAsync(entity, cancellationToken);
 4858        await dbContext.SaveChangesAsync(cancellationToken);
 4859    }
 60
 61    /// <summary>
 62    /// Adds the specified entities.
 63    /// </summary>
 64    /// <param name="entities">The entities to save.</param>
 65    /// <param name="cancellationToken">The cancellation token.</param>
 66    public async Task AddManyAsync(
 67        IEnumerable<TEntity> entities,
 68        CancellationToken cancellationToken = default)
 69    {
 070        await AddManyAsync(entities, null, cancellationToken);
 071    }
 72
 73    /// <summary>
 74    /// Adds the specified entities.
 75    /// </summary>
 76    /// <param name="entities">The entities to save.</param>
 77    /// <param name="onSaving">The callback to invoke before saving the entity.</param>
 78    /// <param name="cancellationToken">The cancellation token.</param>
 79    public async Task AddManyAsync(
 80        IEnumerable<TEntity> entities,
 81        Func<TDbContext, TEntity, CancellationToken, ValueTask>? onSaving = null,
 82        CancellationToken cancellationToken = default)
 83    {
 32384        var entityList = entities.ToList();
 85
 32386        if (entityList.Count == 0)
 087            return;
 88
 32389        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 90
 32391        if (onSaving != null)
 92        {
 509293            var savingTasks = entityList.Select(entity => onSaving(dbContext, entity, cancellationToken).AsTask()).ToLis
 32394            await Task.WhenAll(savingTasks);
 95        }
 96
 32397        await dbContext.BulkInsertAsync(entityList, cancellationToken);
 32398    }
 99
 100    /// <summary>
 101    /// Saves the entity.
 102    /// </summary>
 103    /// <param name="entity">The entity to save.</param>
 104    /// <param name="keySelector">The key selector to get the primary key property.</param>
 105    /// <param name="cancellationToken">The cancellation token.</param>
 0106    public async Task SaveAsync(TEntity entity, Expression<Func<TEntity, string>> keySelector, CancellationToken cancell
 107
 108    /// <summary>
 109    /// Saves the entity.
 110    /// </summary>
 111    /// <param name="entity">The entity to save.</param>
 112    /// <param name="keySelector">The key selector to get the primary key property.</param>
 113    /// <param name="onSaving">The callback to invoke before saving the entity.</param>
 114    /// <param name="cancellationToken">The cancellation token.</param>
 115    public async Task SaveAsync(TEntity entity, Expression<Func<TEntity, string>> keySelector, Func<TDbContext, TEntity,
 116    {
 396117        await Semaphore.WaitAsync(cancellationToken); // Asynchronous wait
 118
 119        try
 120        {
 396121            await using var dbContext = await CreateDbContextAsync(cancellationToken);
 122
 396123            if (onSaving != null)
 396124                await onSaving(dbContext, entity, cancellationToken);
 125
 396126            var set = dbContext.Set<TEntity>();
 396127            var lambda = keySelector.BuildEqualsExpression(entity);
 396128            var exists = await set.AnyAsync(lambda, cancellationToken);
 396129            set.Entry(entity).State = exists ? EntityState.Modified : EntityState.Added;
 396130            await dbContext.SaveChangesAsync(cancellationToken);
 396131        }
 0132        catch (Exception ex)
 133        {
 0134            var handler = serviceProvider.GetService<IDbExceptionHandler>();
 135
 0136            if (handler != null)
 137            {
 0138                var context = new DbUpdateExceptionContext(ex, cancellationToken);
 0139                await handler.HandleAsync(context);
 140            }
 141
 0142            throw;
 143        }
 144        finally
 145        {
 396146            Semaphore.Release();
 147        }
 396148    }
 149
 150    /// <summary>
 151    /// Saves the specified entities.
 152    /// </summary>
 153    /// <param name="entities">The entities to save.</param>
 154    /// <param name="keySelector">The key selector to get the primary key property.</param>
 155    /// <param name="cancellationToken">The cancellation token.</param>
 0156    public async Task SaveManyAsync(IEnumerable<TEntity> entities, Expression<Func<TEntity, string>> keySelector, Cancel
 157
 158    /// <summary>
 159    /// Saves the specified entities.
 160    /// </summary>
 161    /// <param name="entities">The entities to save.</param>
 162    /// <param name="keySelector">The key selector to get the primary key property.</param>
 163    /// <param name="onSaving">The callback to invoke before saving the entity.</param>
 164    /// <param name="cancellationToken">The cancellation token.</param>
 165    public async Task SaveManyAsync(
 166        IEnumerable<TEntity> entities,
 167        Expression<Func<TEntity, string>> keySelector,
 168        Func<TDbContext, TEntity, CancellationToken, ValueTask>? onSaving = null,
 169        CancellationToken cancellationToken = default)
 170    {
 1385171        var entityList = entities.ToList();
 172
 1385173        if (entityList.Count == 0)
 475174            return;
 175
 910176        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 177
 910178        if (onSaving != null)
 179        {
 3981180            var savingTasks = entityList.Select(entity => onSaving(dbContext, entity, cancellationToken).AsTask()).ToLis
 910181            await Task.WhenAll(savingTasks);
 182        }
 183
 184        // When doing a custom SQL query (Bulk Upsert), none of the installed query filters will be applied. Hence, we a
 910185        var tenantId = serviceProvider.GetRequiredService<ITenantAccessor>().Tenant?.Id.NullIfEmpty();
 7962186        foreach (var entity in entityList)
 187        {
 3071188            if (entity is Entity entityWithTenant)
 3071189                entityWithTenant.TenantId = tenantId;
 190        }
 191
 192        try
 193        {
 910194            await dbContext.BulkUpsertAsync(entityList, keySelector, cancellationToken);
 910195        }
 0196        catch (Exception ex)
 197        {
 0198            var handler = serviceProvider.GetService<IDbExceptionHandler>();
 199
 0200            if (handler != null)
 201            {
 0202                var context = new DbUpdateExceptionContext(ex, cancellationToken);
 0203                await handler.HandleAsync(context);
 204            }
 205
 0206            throw;
 207        }
 1385208    }
 209
 210    /// <summary>
 211    /// Updates the entity.
 212    /// </summary>
 213    /// <param name="entity">The entity to update.</param>
 214    /// <param name="cancellationToken">The cancellation token.</param>
 215    public Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default)
 216    {
 0217        return UpdateAsync(entity, null, cancellationToken);
 218    }
 219
 220    /// <summary>
 221    /// Updates the entity.
 222    /// </summary>
 223    /// <param name="entity">The entity to update.</param>
 224    /// <param name="onSaving">The callback to invoke before saving the entity.</param>
 225    /// <param name="cancellationToken">The cancellation token.</param>
 226    public async Task UpdateAsync(TEntity entity, Func<TDbContext, TEntity, CancellationToken, ValueTask>? onSaving, Can
 227    {
 0228        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 229
 0230        if (onSaving != null)
 0231            await onSaving(dbContext, entity, cancellationToken);
 232
 0233        var set = dbContext.Set<TEntity>();
 0234        set.Entry(entity).State = EntityState.Modified;
 0235        await dbContext.SaveChangesAsync(cancellationToken);
 0236    }
 237
 238    /// <summary>
 239    /// Updates specific properties of an entity in the database.
 240    /// </summary>
 241    /// <param name="entity">The entity to update.</param>
 242    /// <param name="properties">An array of expressions indicating the properties to update.</param>
 243    /// <param name="cancellationToken">The cancellation token.</param>
 244    /// <returns>A task that represents the asynchronous operation.</returns>
 245    public async Task UpdatePartialAsync(TEntity entity, Expression<Func<TEntity, object>>[] properties, CancellationTok
 246    {
 0247        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 0248        dbContext.Attach(entity);
 249
 0250        foreach (var property in properties)
 0251            dbContext.Entry(entity).Property(property).IsModified = true;
 252
 0253        await dbContext.SaveChangesAsync(cancellationToken);
 0254    }
 255
 256    /// <summary>
 257    /// Finds the entity matching the specified predicate.
 258    /// </summary>
 259    /// <param name="predicate">The predicate to use.</param>
 260    /// <param name="cancellationToken">The cancellation token.</param>
 261    /// <returns>The entity if found, otherwise <c>null</c>.</returns>
 0262    public async Task<TEntity?> FindAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken
 263
 264    /// <summary>
 265    /// Finds the entity matching the specified predicate.
 266    /// </summary>
 267    /// <param name="predicate">The predicate to use.</param>
 268    /// <param name="onLoading">A callback to run after the entity is loaded</param>
 269    /// <param name="cancellationToken">The cancellation token.</param>
 270    /// <returns></returns>
 271    public async Task<TEntity?> FindAsync(Expression<Func<TEntity, bool>> predicate, Func<TDbContext, TEntity?, TEntity?
 272    {
 0273        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 0274        var set = dbContext.Set<TEntity>().AsNoTracking();
 0275        var entity = await set.FirstOrDefaultAsync(predicate, cancellationToken);
 276
 0277        if (entity == null)
 0278            return null;
 279
 0280        if (onLoading != null)
 0281            entity = onLoading.Invoke(dbContext, entity);
 282
 0283        return entity;
 0284    }
 285
 286    /// <summary>
 287    /// Finds a single entity using a query
 288    /// </summary>
 289    /// <param name="query">The query to use</param>
 290    /// <param name="onLoading">A callback to run after the entity is loaded</param>
 291    /// <param name="cancellationToken">The cancellation token</param>
 292    /// <returns>The entity if found, otherwise <c>null</c></returns>
 293    public async Task<TEntity?> FindAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, Func<TDbContext, TEntity
 294    {
 0295        return await FindAsync(query, onLoading, false, cancellationToken);
 0296    }
 297
 298    /// <summary>
 299    /// Finds a single entity using a query
 300    /// </summary>
 301    /// <param name="query">The query to use</param>
 302    /// <param name="onLoading">A callback to run after the entity is loaded</param>
 303    /// <param name="tenantAgnostic">Define is the request should be tenant agnostic or not</param>
 304    /// <param name="cancellationToken">The cancellation token</param>
 305    /// <returns>The entity if found, otherwise <c>null</c></returns>
 306    public async Task<TEntity?> FindAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, Func<TDbContext, TEntity
 307    {
 2308        return await QueryAsync(query, onLoading, tenantAgnostic, cancellationToken).FirstOrDefault();
 2309    }
 310
 311    /// <summary>
 312    /// Finds a single entity using a query
 313    /// </summary>
 314    /// <param name="query">The query to use</param>
 315    /// <param name="cancellationToken">The cancellation token</param>
 316    /// <returns>The entity if found, otherwise <c>null</c></returns>
 317    public async Task<TEntity?> FindAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancel
 318    {
 0319        return await FindAsync(query, false, cancellationToken);
 0320    }
 321
 322    /// <summary>
 323    /// Finds a single entity using a query
 324    /// </summary>
 325    /// <param name="query">The query to use</param>
 326    /// <param name="tenantAgnostic">Define is the request should be tenant agnostic or not</param>
 327    /// <param name="cancellationToken">The cancellation token</param>
 328    /// <returns>The entity if found, otherwise <c>null</c></returns>
 329    public async Task<TEntity?> FindAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, bool tenantAgnostic = fa
 330    {
 0331        return await QueryAsync(query, tenantAgnostic, cancellationToken).FirstOrDefault();
 0332    }
 333
 334    /// <summary>
 335    /// Finds a list of entities using a query
 336    /// </summary>
 0337    public async Task<IEnumerable<TEntity>> FindManyAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken c
 338
 339    /// <summary>
 340    /// Finds a list of entities using a query
 341    /// </summary>
 342    public async Task<IEnumerable<TEntity>> FindManyAsync(Expression<Func<TEntity, bool>> predicate, Action<TDbContext, 
 343    {
 0344        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 0345        var set = dbContext.Set<TEntity>().AsNoTracking();
 0346        var entities = await set.Where(predicate).ToListAsync(cancellationToken);
 347
 0348        if (onLoading != null)
 0349            foreach (var entity in entities)
 0350                onLoading(dbContext, entity);
 351
 0352        return entities;
 0353    }
 354
 355    /// <summary>
 356    /// Finds a list of entities using a query
 357    /// </summary>
 358    public async Task<Page<TEntity>> FindManyAsync<TKey>(
 359        Expression<Func<TEntity, bool>> predicate,
 360        Expression<Func<TEntity, TKey>> orderBy,
 361        OrderDirection orderDirection = OrderDirection.Ascending,
 362        PageArgs? pageArgs = null,
 363        CancellationToken cancellationToken = default) =>
 0364        await FindManyAsync(predicate, orderBy, orderDirection, pageArgs, null, cancellationToken);
 365
 366    /// <summary>
 367    /// Returns a list of entities using a query
 368    /// </summary>
 369    public async Task<Page<TEntity>> FindManyAsync<TKey>(
 370        Expression<Func<TEntity, bool>>? predicate,
 371        Expression<Func<TEntity, TKey>>? orderBy,
 372        OrderDirection orderDirection = OrderDirection.Ascending,
 373        PageArgs? pageArgs = null,
 374        Func<TDbContext, TEntity?, TEntity?>? onLoading = null,
 375        CancellationToken cancellationToken = default)
 376    {
 0377        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 0378        var set = dbContext.Set<TEntity>().AsNoTracking();
 379
 0380        if (predicate != null)
 0381            set = set.Where(predicate);
 382
 0383        if (orderBy != null)
 0384            set = orderDirection switch
 0385            {
 0386                OrderDirection.Ascending => set.OrderBy(orderBy),
 0387                OrderDirection.Descending => set.OrderByDescending(orderBy),
 0388                _ => set.OrderBy(orderBy)
 0389            };
 390
 0391        var page = await set.PaginateAsync(pageArgs);
 392
 0393        if (onLoading != null)
 0394            page = page with
 0395            {
 0396                Items = page.Items.Select(x => onLoading(dbContext, x)!).ToList()
 0397            };
 398
 0399        return page;
 0400    }
 401
 402    public Task<IEnumerable<TEntity>> ListAsync(CancellationToken cancellationToken = default)
 403    {
 0404        return ListAsync(null, cancellationToken);
 405    }
 406
 407    public async Task<IEnumerable<TEntity>> ListAsync(Action<TDbContext, TEntity?>? onLoading = null, CancellationToken 
 408    {
 0409        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 0410        var set = dbContext.Set<TEntity>().AsNoTracking();
 0411        var entities = await set.ToListAsync(cancellationToken);
 412
 0413        if (onLoading != null)
 0414            foreach (var entity in entities)
 0415                onLoading(dbContext, entity);
 416
 0417        return entities;
 0418    }
 419
 420    /// <summary>
 421    /// Finds a single entity using a query.
 422    /// </summary>
 423    /// <returns>True if the entity was found, otherwise false.</returns>
 424    public async Task<bool> DeleteAsync(TEntity entity, CancellationToken cancellationToken = default)
 425    {
 0426        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 0427        var set = dbContext.Set<TEntity>();
 0428        set.Attach(entity).State = EntityState.Deleted;
 0429        return await dbContext.SaveChangesAsync(cancellationToken) == 1;
 0430    }
 431
 432    /// <summary>
 433    /// Deletes entities using a predicate.
 434    /// </summary>
 435    /// <returns>The number of entities deleted.</returns>
 436    public async Task<long> DeleteWhereAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationTo
 437    {
 4438        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 4439        var set = dbContext.Set<TEntity>().AsNoTracking();
 4440        return await set.Where(predicate).ExecuteDeleteAsync(cancellationToken);
 4441    }
 442
 443    /// <summary>
 444    /// Deletes entities using a query.
 445    /// </summary>
 446    /// <returns>The number of entities deleted.</returns>
 447    public async Task<long> DeleteWhereAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken can
 448    {
 99449        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 99450        var set = dbContext.Set<TEntity>().AsNoTracking();
 99451        var queryable = query(set.AsQueryable());
 99452        return await queryable.ExecuteDeleteAsync(cancellationToken);
 99453    }
 454
 455    /// <summary>
 456    /// Queries the database using a query.
 457    /// </summary>
 458    public async Task<IEnumerable<TEntity>> QueryAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, Cancellatio
 459    {
 47460        return await QueryAsync(query, null, false, cancellationToken);
 47461    }
 462
 463    /// <summary>
 464    /// Queries the database using a query.
 465    /// </summary>
 466    public async Task<IEnumerable<TEntity>> QueryAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, bool tenant
 467    {
 0468        return await QueryAsync(query, null, tenantAgnostic, cancellationToken);
 0469    }
 470
 471    /// <summary>
 472    /// Queries the database using a query and a selector.
 473    /// </summary>
 474    public async Task<IEnumerable<TEntity>> QueryAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, Func<TDbCon
 475    {
 167476        return await QueryAsync(query, onLoading, false, cancellationToken);
 167477    }
 478
 479    /// <summary>
 480    /// Queries the database using a query and a selector.
 481    /// </summary>
 482    public async Task<IEnumerable<TEntity>> QueryAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, Func<TDbCon
 483    {
 4197484        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 4197485        var asNoTracking = onLoading == null;
 4197486        var set = asNoTracking ? dbContext.Set<TEntity>().AsNoTracking() : dbContext.Set<TEntity>();
 4197487        var queryable = query(set.AsQueryable());
 488
 4197489        if (ignoreQueryFilters)
 3490            queryable = queryable.IgnoreQueryFilters();
 491
 4197492        var entities = await queryable.ToListAsync(cancellationToken);
 493
 4197494        if (onLoading != null)
 495        {
 7713496            var loadingTasks = entities.Select(entity => onLoading(dbContext, entity, cancellationToken).AsTask()).ToLis
 4150497            await Task.WhenAll(loadingTasks);
 498        }
 499
 4197500        return entities;
 4197501    }
 502
 503    /// <summary>
 504    /// Queries the database using a query and a selector.
 505    /// </summary>
 506    public async Task<IEnumerable<TResult>> QueryAsync<TResult>(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, Ex
 507    {
 8508        return await QueryAsync(query, selector, false, cancellationToken);
 8509    }
 510
 511    /// <summary>
 512    /// Queries the database using a query and a selector.
 513    /// </summary>
 514    public async Task<IEnumerable<TResult>> QueryAsync<TResult>(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, Ex
 515    {
 8516        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 8517        var set = dbContext.Set<TEntity>().AsNoTracking();
 8518        var queryable = query(set.AsQueryable());
 519
 8520        if (ignoreQueryFilters)
 0521            queryable = queryable.IgnoreQueryFilters();
 522
 8523        queryable = query(queryable);
 8524        return await queryable.Select(selector).ToListAsync(cancellationToken);
 8525    }
 526
 527    /// <summary>
 528    /// Counts the number of entities matching a query.
 529    /// </summary>
 530    public async Task<long> CountAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, CancellationToken cancellat
 531    {
 0532        return await CountAsync(query, false, cancellationToken);
 0533    }
 534
 535    /// <summary>
 536    /// Counts the number of entities matching a query.
 537    /// </summary>
 538    public async Task<long> CountAsync(Func<IQueryable<TEntity>, IQueryable<TEntity>> query, bool ignoreQueryFilters = f
 539    {
 0540        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 0541        var set = dbContext.Set<TEntity>().AsNoTracking();
 0542        var queryable = query(set.AsQueryable());
 543
 0544        if (ignoreQueryFilters)
 0545            queryable = queryable.IgnoreQueryFilters();
 546
 0547        queryable = query(queryable);
 0548        return await queryable.LongCountAsync(cancellationToken: cancellationToken);
 0549    }
 550
 551    /// <summary>
 552    /// Checks if any entities exist.
 553    /// </summary>
 554    public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = de
 555    {
 0556        return await AnyAsync(predicate, false, cancellationToken);
 0557    }
 558
 559    /// <summary>
 560    /// Checks if any entities exist.
 561    /// </summary>
 562    public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> predicate, bool ignoreQueryFilters = false, Cancell
 563    {
 0564        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 0565        var set = dbContext.Set<TEntity>().AsNoTracking();
 0566        return await set.AnyAsync(predicate, cancellationToken);
 0567    }
 568
 569    /// <summary>
 570    /// Counts the number of entities matching a predicate.
 571    /// </summary>
 572    /// <param name="predicate">The predicate.</param>
 573    /// <param name="cancellationToken">The cancellation token.</param>
 574    public async Task<long> CountAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = 
 575    {
 0576        return await CountAsync(predicate, false, cancellationToken);
 0577    }
 578
 579    /// <summary>
 580    /// Counts the number of entities matching a predicate.
 581    /// </summary>
 582    /// <param name="predicate">The predicate.</param>
 583    /// <param name="ignoreQueryFilters">Whether to ignore query filters.</param>
 584    /// <param name="cancellationToken">The cancellation token.</param>
 585    public async Task<long> CountAsync(Expression<Func<TEntity, bool>> predicate, bool ignoreQueryFilters = false, Cance
 586    {
 0587        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 0588        var queryable = dbContext.Set<TEntity>().AsNoTracking();
 589
 0590        if (ignoreQueryFilters)
 0591            queryable = queryable.IgnoreQueryFilters();
 592
 0593        return await queryable.CountAsync(predicate, cancellationToken);
 0594    }
 595
 596    /// <summary>
 597    /// Counts the distinct number of entities matching a predicate.
 598    /// </summary>
 599    /// <param name="predicate">The predicate.</param>
 600    /// <param name="propertySelector">The property selector to distinct by.</param>
 601    /// <param name="cancellationToken">The cancellation token.</param>
 602    public async Task<long> CountAsync<TProperty>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, TP
 603    {
 0604        return await CountAsync(predicate, propertySelector, false, cancellationToken);
 0605    }
 606
 607    /// <summary>
 608    /// Counts the distinct number of entities matching a predicate.
 609    /// </summary>
 610    /// <param name="predicate">The predicate.</param>
 611    /// <param name="propertySelector">The property selector to distinct by.</param>
 612    /// <param name="ignoreQueryFilters">Whether to ignore query filters.</param>
 613    /// <param name="cancellationToken">The cancellation token.</param>
 614    public async Task<long> CountAsync<TProperty>(Expression<Func<TEntity, bool>> predicate, Expression<Func<TEntity, TP
 615    {
 0616        await using var dbContext = await CreateDbContextAsync(cancellationToken);
 0617        var queryable = dbContext.Set<TEntity>().AsNoTracking();
 618
 0619        if (ignoreQueryFilters)
 0620            queryable = queryable.IgnoreQueryFilters();
 621
 0622        return await queryable
 0623            .Where(predicate)
 0624            .Select(propertySelector)
 0625            .Distinct()
 0626            .CountAsync(cancellationToken);
 0627    }
 628}