From ff58571232fdd1bea2540de1beda8664f79878f1 Mon Sep 17 00:00:00 2001 From: Fati Iseni Date: Fri, 8 Nov 2024 11:46:22 +0100 Subject: [PATCH] Added XML documentation. --- src/Directory.Build.props | 2 +- .../Evaluators/AsNoTrackingEvaluator.cs | 9 +- ...TrackingWithIdentityResolutionEvaluator.cs | 9 +- .../Evaluators/AsSplitQueryEvaluator.cs | 9 +- .../Evaluators/AsTrackingEvaluator.cs | 9 +- .../Evaluators/IgnoreQueryFiltersEvaluator.cs | 10 +- .../Evaluators/IncludeEvaluator.cs | 10 +- .../Evaluators/IncludeStringEvaluator.cs | 10 +- .../Evaluators/LikeEvaluator.cs | 9 +- .../Evaluators/SpecificationEvaluator.cs | 33 +++ .../Extensions/IQueryableExtensions.cs | 39 +++- .../RepositoryBase.cs | 71 ++++++ .../RepositoryWithMapper.cs | 40 ++++ .../Builders/Builder_Flags.cs | 155 ++++++++++++ .../Builders/Builder_Include.cs | 221 +++++++++++++++--- .../Builders/Builder_Like.cs | 40 ++++ .../Builders/Builder_Order.cs | 184 ++++++++++++--- .../Builders/Builder_Paging.cs | 64 +++++ .../Builders/Builder_Select.cs | 14 ++ .../Builders/Builder_Where.cs | 48 +++- .../IncludableSpecificationBuilder.cs | 11 + .../Builders/SpecificationBuilder.cs | 50 ++++ .../Evaluators/IEvaluator.cs | 10 + .../Evaluators/IInMemoryEvaluator.cs | 10 + .../Evaluators/LikeMemoryEvaluator.cs | 9 +- .../Evaluators/OrderEvaluator.cs | 10 +- .../Evaluators/PaginationExtensions.cs | 41 +++- .../SpecificationInMemoryEvaluator.cs | 34 +++ .../Evaluators/WhereEvaluator.cs | 11 +- .../ConcurrentSelectorsException.cs | 10 + .../Exceptions/EntityNotFoundException.cs | 15 ++ .../Exceptions/InvalidLikePatternException.cs | 12 + .../Exceptions/SelectorNotFoundException.cs | 10 + .../Expressions/IncludeExpression.cs | 16 ++ .../Expressions/IncludeType.cs | 10 + .../Expressions/LikeExpression.cs | 42 ++++ .../Expressions/OrderExpression.cs | 32 +++ .../Expressions/OrderType.cs | 18 ++ .../Expressions/SelectType.cs | 10 + .../Expressions/WhereExpression.cs | 22 ++ .../IProjectionRepository.cs | 42 ++++ src/QuerySpecification/IReadRepositoryBase.cs | 131 +++++++++++ src/QuerySpecification/IRepositoryBase.cs | 44 ++++ src/QuerySpecification/Paging/PagedResult.cs | 16 ++ src/QuerySpecification/Paging/Pagination.cs | 79 +++++++ .../Paging/PaginationSettings.cs | 23 ++ src/QuerySpecification/Paging/PagingFilter.cs | 10 + src/QuerySpecification/Specification.cs | 156 ++++++++++++- .../Validators/IValidator.cs | 10 + .../Validators/LikeValidator.cs | 9 +- .../Validators/SpecificationValidator.cs | 24 ++ .../Validators/WhereValidator.cs | 9 +- .../GloblUsings.cs | 2 +- .../Evaluators/SpecificationEvaluatorTests.cs | 15 -- .../SpecificationInMemoryEvaluatorTests.cs | 15 -- 55 files changed, 1833 insertions(+), 121 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 22f5296..a08a952 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -7,7 +7,7 @@ enable latest - false + true false true diff --git a/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsNoTrackingEvaluator.cs b/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsNoTrackingEvaluator.cs index 8dc0554..be517e3 100644 --- a/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsNoTrackingEvaluator.cs +++ b/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsNoTrackingEvaluator.cs @@ -1,10 +1,17 @@ namespace Pozitron.QuerySpecification; +/// +/// Evaluator to apply AsNoTracking to the query if the specification has AsNoTracking set to true. +/// public sealed class AsNoTrackingEvaluator : IEvaluator { - private AsNoTrackingEvaluator() { } + /// + /// Gets the singleton instance of the class. + /// public static AsNoTrackingEvaluator Instance = new(); + private AsNoTrackingEvaluator() { } + /// public IQueryable Evaluate(IQueryable source, Specification specification) where T : class { if (specification.AsNoTracking) diff --git a/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsNoTrackingWithIdentityResolutionEvaluator.cs b/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsNoTrackingWithIdentityResolutionEvaluator.cs index 1a384ad..5232a1c 100644 --- a/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsNoTrackingWithIdentityResolutionEvaluator.cs +++ b/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsNoTrackingWithIdentityResolutionEvaluator.cs @@ -1,10 +1,17 @@ namespace Pozitron.QuerySpecification; +/// +/// Evaluator to apply AsNoTracking to the query if the specification has AsNoTracking set to true. +/// public sealed class AsNoTrackingWithIdentityResolutionEvaluator : IEvaluator { - private AsNoTrackingWithIdentityResolutionEvaluator() { } + /// + /// Gets the singleton instance of the class. + /// public static AsNoTrackingWithIdentityResolutionEvaluator Instance = new(); + private AsNoTrackingWithIdentityResolutionEvaluator() { } + /// public IQueryable Evaluate(IQueryable source, Specification specification) where T : class { if (specification.AsNoTrackingWithIdentityResolution) diff --git a/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsSplitQueryEvaluator.cs b/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsSplitQueryEvaluator.cs index 34875ce..a831452 100644 --- a/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsSplitQueryEvaluator.cs +++ b/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsSplitQueryEvaluator.cs @@ -1,10 +1,17 @@ namespace Pozitron.QuerySpecification; +/// +/// Evaluator to apply AsSplitQuery to the query if the specification has AsSplitQuery set to true. +/// public sealed class AsSplitQueryEvaluator : IEvaluator { - private AsSplitQueryEvaluator() { } + /// + /// Gets the singleton instance of the class. + /// public static AsSplitQueryEvaluator Instance = new(); + private AsSplitQueryEvaluator() { } + /// public IQueryable Evaluate(IQueryable source, Specification specification) where T : class { if (specification.AsSplitQuery) diff --git a/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsTrackingEvaluator.cs b/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsTrackingEvaluator.cs index 151d35f..64d09d5 100644 --- a/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsTrackingEvaluator.cs +++ b/src/QuerySpecification.EntityFrameworkCore/Evaluators/AsTrackingEvaluator.cs @@ -1,10 +1,17 @@ namespace Pozitron.QuerySpecification; +/// +/// Evaluator to apply AsTracking to the query if the specification has AsTracking set to true. +/// public sealed class AsTrackingEvaluator : IEvaluator { - private AsTrackingEvaluator() { } + /// + /// Gets the singleton instance of the class. + /// public static AsTrackingEvaluator Instance = new(); + private AsTrackingEvaluator() { } + /// public IQueryable Evaluate(IQueryable source, Specification specification) where T : class { if (specification.AsTracking) diff --git a/src/QuerySpecification.EntityFrameworkCore/Evaluators/IgnoreQueryFiltersEvaluator.cs b/src/QuerySpecification.EntityFrameworkCore/Evaluators/IgnoreQueryFiltersEvaluator.cs index 454b6d6..4abc680 100644 --- a/src/QuerySpecification.EntityFrameworkCore/Evaluators/IgnoreQueryFiltersEvaluator.cs +++ b/src/QuerySpecification.EntityFrameworkCore/Evaluators/IgnoreQueryFiltersEvaluator.cs @@ -1,10 +1,18 @@ namespace Pozitron.QuerySpecification; +/// +/// Evaluator to apply IgnoreQueryFilters to the query if the specification has IgnoreQueryFilters set to true. +/// public sealed class IgnoreQueryFiltersEvaluator : IEvaluator { - private IgnoreQueryFiltersEvaluator() { } + + /// + /// Gets the singleton instance of the class. + /// public static IgnoreQueryFiltersEvaluator Instance = new(); + private IgnoreQueryFiltersEvaluator() { } + /// public IQueryable Evaluate(IQueryable source, Specification specification) where T : class { if (specification.IgnoreQueryFilters) diff --git a/src/QuerySpecification.EntityFrameworkCore/Evaluators/IncludeEvaluator.cs b/src/QuerySpecification.EntityFrameworkCore/Evaluators/IncludeEvaluator.cs index c6ca967..5e8fce3 100644 --- a/src/QuerySpecification.EntityFrameworkCore/Evaluators/IncludeEvaluator.cs +++ b/src/QuerySpecification.EntityFrameworkCore/Evaluators/IncludeEvaluator.cs @@ -6,6 +6,9 @@ namespace Pozitron.QuerySpecification; +/// +/// Evaluates a specification to include navigation properties. +/// public sealed class IncludeEvaluator : IEvaluator { private static readonly MethodInfo _includeMethodInfo = typeof(EntityFrameworkQueryableExtensions) @@ -33,9 +36,14 @@ private static readonly MethodInfo _thenIncludeAfterEnumerableMethodInfo private readonly record struct CacheKey(Type EntityType, Type PropertyType, Type? PreviousReturnType); private static readonly ConcurrentDictionary> _cache = new(); - private IncludeEvaluator() { } + + /// + /// Gets the singleton instance of the class. + /// public static IncludeEvaluator Instance = new(); + private IncludeEvaluator() { } + /// public IQueryable Evaluate(IQueryable source, Specification specification) where T : class { Type? previousReturnType = null; diff --git a/src/QuerySpecification.EntityFrameworkCore/Evaluators/IncludeStringEvaluator.cs b/src/QuerySpecification.EntityFrameworkCore/Evaluators/IncludeStringEvaluator.cs index a185481..ff7e452 100644 --- a/src/QuerySpecification.EntityFrameworkCore/Evaluators/IncludeStringEvaluator.cs +++ b/src/QuerySpecification.EntityFrameworkCore/Evaluators/IncludeStringEvaluator.cs @@ -1,10 +1,18 @@ namespace Pozitron.QuerySpecification; +/// +/// Evaluates a specification to include navigation properties specified by string paths. +/// public sealed class IncludeStringEvaluator : IEvaluator { - private IncludeStringEvaluator() { } + + /// + /// Gets the singleton instance of the class. + /// public static IncludeStringEvaluator Instance = new(); + private IncludeStringEvaluator() { } + /// public IQueryable Evaluate(IQueryable source, Specification specification) where T : class { foreach (var item in specification.Items) diff --git a/src/QuerySpecification.EntityFrameworkCore/Evaluators/LikeEvaluator.cs b/src/QuerySpecification.EntityFrameworkCore/Evaluators/LikeEvaluator.cs index b0f3285..e2592c3 100644 --- a/src/QuerySpecification.EntityFrameworkCore/Evaluators/LikeEvaluator.cs +++ b/src/QuerySpecification.EntityFrameworkCore/Evaluators/LikeEvaluator.cs @@ -16,11 +16,19 @@ This was the previous implementation. We're trying to avoid allocations of LikeE Instead of GroupBy, we have a single array, sorted by group, and we slice it to get the groups. */ +/// +/// Evaluates a specification to apply "like" expressions for filtering. +/// public sealed class LikeEvaluator : IEvaluator { private LikeEvaluator() { } + + /// + /// Gets the singleton instance of the class. + /// public static LikeEvaluator Instance = new(); + /// public IQueryable Evaluate(IQueryable source, Specification specification) where T : class { var likeCount = GetLikeCount(specification); @@ -103,4 +111,3 @@ private static void FillSorted(Specification specification, Span } } } - diff --git a/src/QuerySpecification.EntityFrameworkCore/Evaluators/SpecificationEvaluator.cs b/src/QuerySpecification.EntityFrameworkCore/Evaluators/SpecificationEvaluator.cs index 89d0854..384db85 100644 --- a/src/QuerySpecification.EntityFrameworkCore/Evaluators/SpecificationEvaluator.cs +++ b/src/QuerySpecification.EntityFrameworkCore/Evaluators/SpecificationEvaluator.cs @@ -1,11 +1,23 @@ namespace Pozitron.QuerySpecification; +/// +/// Evaluates specifications by applying a series of evaluators. +/// public class SpecificationEvaluator { + /// + /// Gets the default instance of the class. + /// public static SpecificationEvaluator Default = new(); + /// + /// Gets the list of evaluators. + /// protected List Evaluators { get; } + /// + /// Initializes a new instance of the class. + /// public SpecificationEvaluator() { Evaluators = @@ -23,11 +35,24 @@ public SpecificationEvaluator() ]; } + /// + /// Initializes a new instance of the class with the specified evaluators. + /// + /// The evaluators to use. public SpecificationEvaluator(IEnumerable evaluators) { Evaluators = evaluators.ToList(); } + /// + /// Evaluates the given specification on the provided queryable source and returns the result. + /// + /// The type of the entity. + /// The type of the result. + /// The queryable source. + /// The specification to evaluate. + /// Whether to ignore paging settings (Take/Skip) defined in the specification. + /// The evaluated queryable result. public virtual IQueryable Evaluate( IQueryable source, Specification specification, @@ -54,6 +79,14 @@ public virtual IQueryable Evaluate( : resultQuery.ApplyPaging(specification); } + /// + /// Evaluates the given specification on the provided queryable source and returns the result. + /// + /// The type of the entity. + /// The queryable source. + /// The specification to evaluate. + /// Whether to ignore paging settings (Take/Skip) defined in the specification. + /// The evaluated queryable result. public virtual IQueryable Evaluate( IQueryable source, Specification specification, diff --git a/src/QuerySpecification.EntityFrameworkCore/Extensions/IQueryableExtensions.cs b/src/QuerySpecification.EntityFrameworkCore/Extensions/IQueryableExtensions.cs index af028ca..6a38de1 100644 --- a/src/QuerySpecification.EntityFrameworkCore/Extensions/IQueryableExtensions.cs +++ b/src/QuerySpecification.EntityFrameworkCore/Extensions/IQueryableExtensions.cs @@ -1,7 +1,18 @@ namespace Pozitron.QuerySpecification; +/// +/// Provides extension methods for to apply specifications and pagination. +/// public static class IQueryableExtensions { + /// + /// Applies the given specification to the queryable source. + /// + /// The type of the entity. + /// The queryable source. + /// The specification to apply. + /// The specification evaluator to use. If null, the default evaluator is used. + /// The queryable source with the specification applied. public static IQueryable WithSpecification( this IQueryable source, Specification specification, @@ -12,6 +23,15 @@ public static IQueryable WithSpecification( return evaluator.Evaluate(source, specification); } + /// + /// Applies the given specification to the queryable source and projects the result to a different type. + /// + /// The type of the entity. + /// The type of the result. + /// The queryable source. + /// The specification to apply. + /// The specification evaluator to use. If null, the default evaluator is used. + /// The queryable source with the specification applied and projected to the result type. public static IQueryable WithSpecification( this IQueryable source, Specification specification, @@ -22,13 +42,30 @@ public static IQueryable WithSpecification( return evaluator.Evaluate(source, specification); } + /// + /// Converts the queryable source to a paged result asynchronously. + /// + /// The type of the entity. + /// The queryable source. + /// The paging filter. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the paged result. public static Task> ToPagedResultAsync( this IQueryable source, PagingFilter filter, CancellationToken cancellationToken = default) where TSource : class - => ToPagedResultAsync(source, filter, PaginationSettings.Default, cancellationToken); + => ToPagedResultAsync(source, filter, PaginationSettings.Default, cancellationToken); + /// + /// Converts the queryable source to a paged result asynchronously with the specified pagination settings. + /// + /// The type of the entity. + /// The queryable source. + /// The paging filter. + /// The pagination settings. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the paged result. public static async Task> ToPagedResultAsync( this IQueryable source, PagingFilter filter, diff --git a/src/QuerySpecification.EntityFrameworkCore/RepositoryBase.cs b/src/QuerySpecification.EntityFrameworkCore/RepositoryBase.cs index bca5de1..ecf0892 100644 --- a/src/QuerySpecification.EntityFrameworkCore/RepositoryBase.cs +++ b/src/QuerySpecification.EntityFrameworkCore/RepositoryBase.cs @@ -1,20 +1,35 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a base repository for accessing and modifying entities. +/// +/// The type of the entity. public abstract class RepositoryBase : IRepositoryBase where T : class { private readonly DbContext _dbContext; private readonly SpecificationEvaluator _evaluator; + /// + /// Initializes a new instance of the class. + /// + /// The database context. protected RepositoryBase(DbContext dbContext) : this(dbContext, SpecificationEvaluator.Default) { } + + /// + /// Initializes a new instance of the class with the specified specification evaluator. + /// + /// The database context. + /// The specification evaluator. protected RepositoryBase(DbContext dbContext, SpecificationEvaluator specificationEvaluator) { _dbContext = dbContext; _evaluator = specificationEvaluator; } + /// public virtual async Task AddAsync(T entity, CancellationToken cancellationToken = default) { _dbContext.Set().Add(entity); @@ -23,6 +38,8 @@ public virtual async Task AddAsync(T entity, CancellationToken cancellationTo return entity; } + + /// public virtual async Task> AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default) { _dbContext.Set().AddRange(entities); @@ -31,106 +48,160 @@ public virtual async Task> AddRangeAsync(IEnumerable entities, return entities; } + + /// public virtual async Task UpdateAsync(T entity, CancellationToken cancellationToken = default) { //dbContext.Set().Update(entity); await SaveChangesAsync(cancellationToken); } + + /// public virtual async Task DeleteAsync(T entity, CancellationToken cancellationToken = default) { _dbContext.Set().Remove(entity); await SaveChangesAsync(cancellationToken); } + + /// public virtual async Task DeleteRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default) { _dbContext.Set().RemoveRange(entities); await SaveChangesAsync(cancellationToken); } + + /// public virtual async Task SaveChangesAsync(CancellationToken cancellationToken = default) { return await _dbContext.SaveChangesAsync(cancellationToken); } + /// public virtual async ValueTask FindAsync(TId id, CancellationToken cancellationToken = default) where TId : notnull { return await _dbContext.Set().FindAsync([id], cancellationToken: cancellationToken); } + + /// public virtual async Task FirstAsync(Specification specification, CancellationToken cancellationToken = default) { var result = await GenerateQuery(specification).FirstOrDefaultAsync(cancellationToken); return result ?? throw new EntityNotFoundException(typeof(T).Name); } + + /// public virtual async Task FirstAsync(Specification specification, CancellationToken cancellationToken = default) { var result = await GenerateQuery(specification).FirstOrDefaultAsync(cancellationToken); return result ?? throw new EntityNotFoundException(typeof(T).Name); } + + /// public virtual async Task FirstOrDefaultAsync(Specification specification, CancellationToken cancellationToken = default) { return await GenerateQuery(specification).FirstOrDefaultAsync(cancellationToken); } + + /// public virtual async Task FirstOrDefaultAsync(Specification specification, CancellationToken cancellationToken = default) { return await GenerateQuery(specification).FirstOrDefaultAsync(cancellationToken); } + + /// public virtual async Task SingleOrDefaultAsync(Specification specification, CancellationToken cancellationToken = default) { return await GenerateQuery(specification).SingleOrDefaultAsync(cancellationToken); } + + /// public virtual async Task SingleOrDefaultAsync(Specification specification, CancellationToken cancellationToken = default) { return await GenerateQuery(specification).SingleOrDefaultAsync(cancellationToken); } + + /// public virtual async Task> ListAsync(CancellationToken cancellationToken = default) { return await _dbContext.Set().ToListAsync(cancellationToken); } + + /// public virtual async Task> ListAsync(Specification specification, CancellationToken cancellationToken = default) { return await GenerateQuery(specification).ToListAsync(cancellationToken); } + + /// public virtual async Task> ListAsync(Specification specification, CancellationToken cancellationToken = default) { return await GenerateQuery(specification).ToListAsync(cancellationToken); } + + /// public virtual async Task CountAsync(CancellationToken cancellationToken = default) { return await _dbContext.Set().CountAsync(cancellationToken); } + + /// public virtual async Task CountAsync(Specification specification, CancellationToken cancellationToken = default) { return await GenerateQuery(specification, true).CountAsync(cancellationToken); } + + /// public virtual async Task CountAsync(Specification specification, CancellationToken cancellationToken = default) { return await GenerateQuery(specification, true).CountAsync(cancellationToken); } + + /// public virtual async Task AnyAsync(CancellationToken cancellationToken = default) { return await _dbContext.Set().AnyAsync(cancellationToken); } + + /// public virtual async Task AnyAsync(Specification specification, CancellationToken cancellationToken = default) { return await GenerateQuery(specification, true).AnyAsync(cancellationToken); } + + /// public virtual async Task AnyAsync(Specification specification, CancellationToken cancellationToken = default) { return await GenerateQuery(specification, true).AnyAsync(cancellationToken); } + /// public virtual IAsyncEnumerable AsAsyncEnumerable(Specification specification) { return GenerateQuery(specification).AsAsyncEnumerable(); } + /// + /// Generates a query based on the specification. + /// + /// The specification to evaluate. + /// Whether to ignore paging settings (Take/Skip) defined in the specification. + /// The generated query. protected virtual IQueryable GenerateQuery(Specification specification, bool ignorePaging = false) { var query = _evaluator.Evaluate(_dbContext.Set(), specification, ignorePaging); return query; } + + /// + /// Generates a query based on the specification. + /// + /// The type of the result. + /// The specification to evaluate. + /// Whether to ignore paging settings (Take/Skip) defined in the specification. + /// The generated query. protected virtual IQueryable GenerateQuery(Specification specification, bool ignorePaging = false) { var query = _evaluator.Evaluate(_dbContext.Set(), specification, ignorePaging); diff --git a/src/QuerySpecification.EntityFrameworkCore/RepositoryWithMapper.cs b/src/QuerySpecification.EntityFrameworkCore/RepositoryWithMapper.cs index 3f1fda6..83be4d6 100644 --- a/src/QuerySpecification.EntityFrameworkCore/RepositoryWithMapper.cs +++ b/src/QuerySpecification.EntityFrameworkCore/RepositoryWithMapper.cs @@ -1,30 +1,64 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a repository with mapping capabilities for projecting entities to different result types. +/// +/// The type of the entity. public abstract class RepositoryWithMapper : RepositoryBase, IProjectionRepository where T : class { private readonly PaginationSettings _paginationSettings = PaginationSettings.Default; + /// + /// Initializes a new instance of the class. + /// + /// The database context. protected RepositoryWithMapper(DbContext dbContext) : base(dbContext) { } + + /// + /// Initializes a new instance of the class with the specified specification evaluator. + /// + /// The database context. + /// The specification evaluator. protected RepositoryWithMapper(DbContext dbContext, SpecificationEvaluator specificationEvaluator) : base(dbContext, specificationEvaluator) { } + + /// + /// Initializes a new instance of the class with the specified pagination settings. + /// + /// The database context. + /// The pagination settings. protected RepositoryWithMapper(DbContext dbContext, PaginationSettings paginationSettings) : base(dbContext) { _paginationSettings = paginationSettings; } + + /// + /// Initializes a new instance of the class with the specified specification evaluator and pagination settings. + /// + /// The database context. + /// The specification evaluator. + /// The pagination settings. protected RepositoryWithMapper(DbContext dbContext, SpecificationEvaluator specificationEvaluator, PaginationSettings paginationSettings) : base(dbContext, specificationEvaluator) { _paginationSettings = paginationSettings; } + /// + /// Maps the source query to the result type. + /// + /// The type of the result. + /// The source query. + /// The mapped query. protected abstract IQueryable Map(IQueryable source); + /// public virtual async Task ProjectToFirstAsync(Specification specification, CancellationToken cancellationToken = default) { var query = GenerateQuery(specification).AsNoTracking(); @@ -35,6 +69,8 @@ public virtual async Task ProjectToFirstAsync(Specification return result ?? throw new EntityNotFoundException(typeof(T).Name); } + + /// public virtual async Task ProjectToFirstOrDefaultAsync(Specification specification, CancellationToken cancellationToken = default) { var query = GenerateQuery(specification).AsNoTracking(); @@ -43,6 +79,8 @@ public virtual async Task ProjectToFirstAsync(Specification return await projectedQuery.FirstOrDefaultAsync(cancellationToken); } + + /// public virtual async Task> ProjectToListAsync(Specification specification, CancellationToken cancellationToken = default) { var query = GenerateQuery(specification).AsNoTracking(); @@ -51,6 +89,8 @@ public virtual async Task> ProjectToListAsync(Specificati return await projectedQuery.ToListAsync(cancellationToken); } + + /// public virtual async Task> ProjectToListAsync(Specification specification, PagingFilter filter, CancellationToken cancellationToken = default) { var query = GenerateQuery(specification, true).AsNoTracking(); diff --git a/src/QuerySpecification/Builders/Builder_Flags.cs b/src/QuerySpecification/Builders/Builder_Flags.cs index c170af9..abd6642 100644 --- a/src/QuerySpecification/Builders/Builder_Flags.cs +++ b/src/QuerySpecification/Builders/Builder_Flags.cs @@ -1,11 +1,29 @@ namespace Pozitron.QuerySpecification; +/// +/// Extension methods for building specifications. +/// public static partial class SpecificationBuilderExtensions { + /// + /// Configures the specification to ignore query filters. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The updated specification builder. public static ISpecificationBuilder IgnoreQueryFilters( this ISpecificationBuilder builder) where T : class => IgnoreQueryFilters(builder, true); + /// + /// Configures the specification to ignore query filters if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder IgnoreQueryFilters( this ISpecificationBuilder builder, bool condition) where T : class @@ -17,10 +35,23 @@ public static ISpecificationBuilder IgnoreQueryFilters( return builder; } + /// + /// Configures the specification to ignore query filters. + /// + /// The type of the entity. + /// The specification builder. + /// The updated specification builder. public static ISpecificationBuilder IgnoreQueryFilters( this ISpecificationBuilder builder) where T : class => IgnoreQueryFilters(builder, true); + /// + /// Configures the specification to ignore query filters if the condition is true. + /// + /// The type of the entity. + /// The specification builder. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder IgnoreQueryFilters( this ISpecificationBuilder builder, bool condition) where T : class @@ -32,10 +63,25 @@ public static ISpecificationBuilder IgnoreQueryFilters( return builder; } + /// + /// Configures the specification to use split queries. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The updated specification builder. public static ISpecificationBuilder AsSplitQuery( this ISpecificationBuilder builder) where T : class => AsSplitQuery(builder, true); + /// + /// Configures the specification to use split queries if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder AsSplitQuery( this ISpecificationBuilder builder, bool condition) where T : class @@ -47,10 +93,23 @@ public static ISpecificationBuilder AsSplitQuery( return builder; } + /// + /// Configures the specification to use split queries. + /// + /// The type of the entity. + /// The specification builder. + /// The updated specification builder. public static ISpecificationBuilder AsSplitQuery( this ISpecificationBuilder builder) where T : class => AsSplitQuery(builder, true); + /// + /// Configures the specification to use split queries if the condition is true. + /// + /// The type of the entity. + /// The specification builder. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder AsSplitQuery( this ISpecificationBuilder builder, bool condition) where T : class @@ -62,10 +121,27 @@ public static ISpecificationBuilder AsSplitQuery( return builder; } + /// + /// Configures the specification to apply NoTracking behavior. + /// It will also disable AsNoTrackingWithIdentityResolution and AsTracking flags. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The updated specification builder. public static ISpecificationBuilder AsNoTracking( this ISpecificationBuilder builder) where T : class => AsNoTracking(builder, true); + /// + /// Configures the specification to apply NoTracking behavior if the condition is true. + /// It will also disable AsNoTrackingWithIdentityResolution and AsTracking flags. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder AsNoTracking( this ISpecificationBuilder builder, bool condition) where T : class @@ -79,10 +155,25 @@ public static ISpecificationBuilder AsNoTracking( return builder; } + /// + /// Configures the specification to apply NoTracking behavior. + /// It will also disable AsNoTrackingWithIdentityResolution and AsTracking flags. + /// + /// The type of the entity. + /// The specification builder. + /// The updated specification builder. public static ISpecificationBuilder AsNoTracking( this ISpecificationBuilder builder) where T : class => AsNoTracking(builder, true); + /// + /// Configures the specification to apply NoTracking behavior if the condition is true. + /// It will also disable AsNoTrackingWithIdentityResolution and AsTracking flags. + /// + /// The type of the entity. + /// The specification builder. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder AsNoTracking( this ISpecificationBuilder builder, bool condition) where T : class @@ -96,10 +187,27 @@ public static ISpecificationBuilder AsNoTracking( return builder; } + /// + /// Configures the specification to apply AsNoTrackingWithIdentityResolution behavior. + /// It will also disable AsNoTracking and AsTracking flags. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The updated specification builder. public static ISpecificationBuilder AsNoTrackingWithIdentityResolution( this ISpecificationBuilder builder) where T : class => AsNoTrackingWithIdentityResolution(builder, true); + /// + /// Configures the specification to apply AsNoTrackingWithIdentityResolution behavior if the condition is true. + /// It will also disable AsNoTracking and AsTracking flags. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder AsNoTrackingWithIdentityResolution( this ISpecificationBuilder builder, bool condition) where T : class @@ -113,10 +221,25 @@ public static ISpecificationBuilder AsNoTrackingWithIdentityResoluti return builder; } + /// + /// Configures the specification to apply AsNoTrackingWithIdentityResolution behavior. + /// It will also disable AsNoTracking and AsTracking flags. + /// + /// The type of the entity. + /// The specification builder. + /// The updated specification builder. public static ISpecificationBuilder AsNoTrackingWithIdentityResolution( this ISpecificationBuilder builder) where T : class => AsNoTrackingWithIdentityResolution(builder, true); + /// + /// Configures the specification to apply AsNoTrackingWithIdentityResolution behavior if the condition is true. + /// It will also disable AsNoTracking and AsTracking flags. + /// + /// The type of the entity. + /// The specification builder. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder AsNoTrackingWithIdentityResolution( this ISpecificationBuilder builder, bool condition) where T : class @@ -130,10 +253,27 @@ public static ISpecificationBuilder AsNoTrackingWithIdentityResolution( return builder; } + /// + /// Configures the specification to apply AsTracking behavior. + /// It will also disable AsNoTrackingWithIdentityResolution and AsNoTracking flags. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The updated specification builder. public static ISpecificationBuilder AsTracking( this ISpecificationBuilder builder) where T : class => AsTracking(builder, true); + /// + /// Configures the specification to apply AsTracking behavior if the condition is true. + /// It will also disable AsNoTrackingWithIdentityResolution and AsNoTracking flags. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder AsTracking( this ISpecificationBuilder builder, bool condition) where T : class @@ -147,10 +287,25 @@ public static ISpecificationBuilder AsTracking( return builder; } + /// + /// Configures the specification to apply AsTracking behavior. + /// It will also disable AsNoTrackingWithIdentityResolution and AsNoTracking flags. + /// + /// The type of the entity. + /// The specification builder. + /// The updated specification builder. public static ISpecificationBuilder AsTracking( this ISpecificationBuilder builder) where T : class => AsTracking(builder, true); + /// + /// Configures the specification to apply AsTracking behavior if the condition is true. + /// It will also disable AsNoTrackingWithIdentityResolution and AsNoTracking flags. + /// + /// The type of the entity. + /// The specification builder. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder AsTracking( this ISpecificationBuilder builder, bool condition) where T : class diff --git a/src/QuerySpecification/Builders/Builder_Include.cs b/src/QuerySpecification/Builders/Builder_Include.cs index c972eb7..a61179d 100644 --- a/src/QuerySpecification/Builders/Builder_Include.cs +++ b/src/QuerySpecification/Builders/Builder_Include.cs @@ -2,11 +2,28 @@ public static partial class SpecificationBuilderExtensions { + /// + /// Adds an Include clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The include string. + /// The updated specification builder. public static ISpecificationBuilder Include( this ISpecificationBuilder builder, string includeString) where T : class => Include(builder, includeString, true); + /// + /// Adds an Include clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The include string. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder Include( this ISpecificationBuilder builder, string includeString, @@ -18,11 +35,27 @@ public static ISpecificationBuilder Include( } return builder; } + + /// + /// Adds an Include clause to the specification. + /// + /// The type of the entity. + /// The specification builder. + /// The include string. + /// The updated specification builder. public static ISpecificationBuilder Include( this ISpecificationBuilder builder, string includeString) where T : class => Include(builder, includeString, true); + /// + /// Adds an Include clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The specification builder. + /// The include string. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder Include( this ISpecificationBuilder builder, string includeString, @@ -35,19 +68,38 @@ public static ISpecificationBuilder Include( return builder; } + /// + /// Adds an Include clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The type of the property. + /// The specification builder. + /// The include expression. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder Include( this ISpecificationBuilder builder, - Expression> includeExpression) where T : class - => Include(builder, includeExpression, true); + Expression> navigationSelector) where T : class + => Include(builder, navigationSelector, true); + /// + /// Adds an Include clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The type of the property. + /// The specification builder. + /// The include expression. + /// The condition to evaluate. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder Include( this ISpecificationBuilder builder, - Expression> includeExpression, + Expression> navigationSelector, bool condition) where T : class { if (condition) { - builder.Specification.AddInternal(ItemType.Include, includeExpression, (int)IncludeType.Include); + builder.Specification.AddInternal(ItemType.Include, navigationSelector, (int)IncludeType.Include); } Specification.IsChainDiscarded = !condition; @@ -55,19 +107,36 @@ public static IIncludableSpecificationBuilder Include + /// Adds an Include clause to the specification. + /// + /// The type of the entity. + /// The type of the property. + /// The specification builder. + /// The include expression. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder Include( this ISpecificationBuilder builder, - Expression> includeExpression) where T : class - => Include(builder, includeExpression, true); + Expression> navigationSelector) where T : class + => Include(builder, navigationSelector, true); + /// + /// Adds an Include clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the property. + /// The specification builder. + /// The include expression. + /// The condition to evaluate. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder Include( this ISpecificationBuilder builder, - Expression> includeExpression, + Expression> navigationSelector, bool condition) where T : class { if (condition) { - builder.Specification.AddInternal(ItemType.Include, includeExpression, (int)IncludeType.Include); + builder.Specification.AddInternal(ItemType.Include, navigationSelector, (int)IncludeType.Include); } Specification.IsChainDiscarded = !condition; @@ -75,103 +144,183 @@ public static IIncludableSpecificationBuilder Include + /// Adds a ThenInclude clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The type of the previous property. + /// The type of the property. + /// The previous includable specification builder. + /// The include expression. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder ThenInclude( - this IIncludableSpecificationBuilder previousBuilder, - Expression> thenIncludeExpression) + this IIncludableSpecificationBuilder builder, + Expression> navigationSelector) where TEntity : class - => ThenInclude(previousBuilder, thenIncludeExpression, true); + => ThenInclude(builder, navigationSelector, true); + /// + /// Adds a ThenInclude clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The type of the previous property. + /// The type of the property. + /// The previous includable specification builder. + /// The include expression. + /// The condition to evaluate. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder ThenInclude( - this IIncludableSpecificationBuilder previousBuilder, - Expression> thenIncludeExpression, + this IIncludableSpecificationBuilder builder, + Expression> navigationSelector, bool condition) where TEntity : class { if (condition && !Specification.IsChainDiscarded) { - previousBuilder.Specification.AddInternal(ItemType.Include, thenIncludeExpression, (int)IncludeType.ThenInclude); + builder.Specification.AddInternal(ItemType.Include, navigationSelector, (int)IncludeType.ThenInclude); } else { Specification.IsChainDiscarded = true; } - var includeBuilder = new IncludableSpecificationBuilder(previousBuilder.Specification); + var includeBuilder = new IncludableSpecificationBuilder(builder.Specification); return includeBuilder; } + /// + /// Adds a ThenInclude clause to the specification. + /// + /// The type of the entity. + /// The type of the previous property. + /// The type of the property. + /// The previous includable specification builder. + /// The include expression. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder ThenInclude( - this IIncludableSpecificationBuilder previousBuilder, - Expression> thenIncludeExpression) + this IIncludableSpecificationBuilder builder, + Expression> navigationSelector) where TEntity : class - => ThenInclude(previousBuilder, thenIncludeExpression, true); + => ThenInclude(builder, navigationSelector, true); + /// + /// Adds a ThenInclude clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the previous property. + /// The type of the property. + /// The previous includable specification builder. + /// The include expression. + /// The condition to evaluate. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder ThenInclude( - this IIncludableSpecificationBuilder previousBuilder, - Expression> thenIncludeExpression, + this IIncludableSpecificationBuilder builder, + Expression> navigationSelector, bool condition) where TEntity : class { if (condition && !Specification.IsChainDiscarded) { - previousBuilder.Specification.AddInternal(ItemType.Include, thenIncludeExpression, (int)IncludeType.ThenInclude); + builder.Specification.AddInternal(ItemType.Include, navigationSelector, (int)IncludeType.ThenInclude); } else { Specification.IsChainDiscarded = true; } - var includeBuilder = new IncludableSpecificationBuilder(previousBuilder.Specification); + var includeBuilder = new IncludableSpecificationBuilder(builder.Specification); return includeBuilder; } + /// + /// Adds a ThenInclude clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The type of the previous property. + /// The type of the property. + /// The previous includable specification builder. + /// The include expression. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder ThenInclude( - this IIncludableSpecificationBuilder> previousBuilder, - Expression> thenIncludeExpression) + this IIncludableSpecificationBuilder> builder, + Expression> navigationSelector) where TEntity : class - => ThenInclude(previousBuilder, thenIncludeExpression, true); + => ThenInclude(builder, navigationSelector, true); + /// + /// Adds a ThenInclude clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The type of the previous property. + /// The type of the property. + /// The previous includable specification builder. + /// The include expression. + /// The condition to evaluate. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder ThenInclude( - this IIncludableSpecificationBuilder> previousBuilder, - Expression> thenIncludeExpression, + this IIncludableSpecificationBuilder> builder, + Expression> navigationSelector, bool condition) where TEntity : class { if (condition && !Specification.IsChainDiscarded) { - previousBuilder.Specification.AddInternal(ItemType.Include, thenIncludeExpression, (int)IncludeType.ThenInclude); + builder.Specification.AddInternal(ItemType.Include, navigationSelector, (int)IncludeType.ThenInclude); } else { Specification.IsChainDiscarded = true; } - var includeBuilder = new IncludableSpecificationBuilder(previousBuilder.Specification); + var includeBuilder = new IncludableSpecificationBuilder(builder.Specification); return includeBuilder; } + /// + /// Adds a ThenInclude clause to the specification. + /// + /// The type of the entity. + /// The type of the previous property. + /// The type of the property. + /// The previous includable specification builder. + /// The include expression. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder ThenInclude( - this IIncludableSpecificationBuilder> previousBuilder, - Expression> thenIncludeExpression) + this IIncludableSpecificationBuilder> builder, + Expression> navigationSelector) where TEntity : class - => ThenInclude(previousBuilder, thenIncludeExpression, true); + => ThenInclude(builder, navigationSelector, true); + /// + /// Adds a ThenInclude clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the previous property. + /// The type of the property. + /// The previous includable specification builder. + /// The include expression. + /// The condition to evaluate. + /// The updated includable specification builder. public static IIncludableSpecificationBuilder ThenInclude( - this IIncludableSpecificationBuilder> previousBuilder, - Expression> thenIncludeExpression, + this IIncludableSpecificationBuilder> builder, + Expression> navigationSelector, bool condition) where TEntity : class { if (condition && !Specification.IsChainDiscarded) { - previousBuilder.Specification.AddInternal(ItemType.Include, thenIncludeExpression, (int)IncludeType.ThenInclude); + builder.Specification.AddInternal(ItemType.Include, navigationSelector, (int)IncludeType.ThenInclude); } else { Specification.IsChainDiscarded = true; } - var includeBuilder = new IncludableSpecificationBuilder(previousBuilder.Specification); + var includeBuilder = new IncludableSpecificationBuilder(builder.Specification); return includeBuilder; } } diff --git a/src/QuerySpecification/Builders/Builder_Like.cs b/src/QuerySpecification/Builders/Builder_Like.cs index deddd4a..9a2a4b1 100644 --- a/src/QuerySpecification/Builders/Builder_Like.cs +++ b/src/QuerySpecification/Builders/Builder_Like.cs @@ -2,6 +2,16 @@ public static partial class SpecificationBuilderExtensions { + /// + /// Adds a Like clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The key selector expression. + /// The pattern to match. + /// The group number. Like clauses within the same group are evaluated using OR logic. + /// The updated specification builder. public static ISpecificationBuilder Like( this ISpecificationBuilder builder, Expression> keySelector, @@ -9,6 +19,17 @@ public static ISpecificationBuilder Like( int group = 1) where T : class => Like(builder, keySelector, pattern, true, group); + /// + /// Adds a Like clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The key selector expression. + /// The pattern to match. + /// The condition to evaluate. + /// The group number. Like clauses within the same group are evaluated using OR logic. + /// The updated specification builder. public static ISpecificationBuilder Like( this ISpecificationBuilder builder, Expression> keySelector, @@ -24,6 +45,15 @@ public static ISpecificationBuilder Like( return builder; } + /// + /// Adds a Like clause to the specification. + /// + /// The type of the entity. + /// The specification builder. + /// The key selector expression. + /// The pattern to match. + /// The group number. Like clauses within the same group are evaluated using OR logic. + /// The updated specification builder. public static ISpecificationBuilder Like( this ISpecificationBuilder builder, Expression> keySelector, @@ -31,6 +61,16 @@ public static ISpecificationBuilder Like( int group = 1) where T : class => Like(builder, keySelector, pattern, true, group); + /// + /// Adds a Like clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The specification builder. + /// The key selector expression. + /// The pattern to match. + /// The condition to evaluate. + /// The group number. Like clauses within the same group are evaluated using OR logic. + /// The updated specification builder. public static ISpecificationBuilder Like( this ISpecificationBuilder builder, Expression> keySelector, diff --git a/src/QuerySpecification/Builders/Builder_Order.cs b/src/QuerySpecification/Builders/Builder_Order.cs index 61e37d4..bf3e15b 100644 --- a/src/QuerySpecification/Builders/Builder_Order.cs +++ b/src/QuerySpecification/Builders/Builder_Order.cs @@ -2,11 +2,28 @@ public static partial class SpecificationBuilderExtensions { + /// + /// Adds an OrderBy clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The key selector expression. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder OrderBy( this ISpecificationBuilder builder, Expression> keySelector) => OrderBy(builder, keySelector, true); + /// + /// Adds an OrderBy clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The key selector expression. + /// The condition to evaluate. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder OrderBy( this ISpecificationBuilder builder, Expression> keySelector, @@ -21,11 +38,26 @@ public static IOrderedSpecificationBuilder OrderBy( return (SpecificationBuilder)builder; } + /// + /// Adds an OrderBy clause to the specification. + /// + /// The type of the entity. + /// The specification builder. + /// The key selector expression. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder OrderBy( this ISpecificationBuilder builder, Expression> keySelector) => OrderBy(builder, keySelector, true); + /// + /// Adds an OrderBy clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The specification builder. + /// The key selector expression. + /// The condition to evaluate. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder OrderBy( this ISpecificationBuilder builder, Expression> keySelector, @@ -40,11 +72,28 @@ public static IOrderedSpecificationBuilder OrderBy( return (SpecificationBuilder)builder; } + /// + /// Adds an OrderBy descending clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The key selector expression. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder OrderByDescending( this ISpecificationBuilder builder, Expression> keySelector) => OrderByDescending(builder, keySelector, true); + /// + /// Adds an OrderByDescending clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The key selector expression. + /// The condition to evaluate. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder OrderByDescending( this ISpecificationBuilder builder, Expression> keySelector, @@ -59,11 +108,26 @@ public static IOrderedSpecificationBuilder OrderByDescending)builder; } + /// + /// Adds an OrderByDescending clause to the specification. + /// + /// The type of the entity. + /// The specification builder. + /// The key selector expression. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder OrderByDescending( this ISpecificationBuilder builder, Expression> keySelector) => OrderByDescending(builder, keySelector, true); + /// + /// Adds an OrderByDescending clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The specification builder. + /// The key selector expression. + /// The condition to evaluate. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder OrderByDescending( this ISpecificationBuilder builder, Expression> keySelector, @@ -78,91 +142,155 @@ public static IOrderedSpecificationBuilder OrderByDescending( return (SpecificationBuilder)builder; } + /// + /// Adds a ThenBy clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The ordered specification builder. + /// The key selector expression. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder ThenBy( - this IOrderedSpecificationBuilder orderedBuilder, - Expression> orderExpression) - => ThenBy(orderedBuilder, orderExpression, true); + this IOrderedSpecificationBuilder builder, + Expression> keySelector) + => ThenBy(builder, keySelector, true); + /// + /// Adds a ThenBy clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The ordered specification builder. + /// The key selector expression. + /// The condition to evaluate. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder ThenBy( - this IOrderedSpecificationBuilder orderedBuilder, - Expression> orderExpression, + this IOrderedSpecificationBuilder builder, + Expression> keySelector, bool condition) { if (condition && !Specification.IsChainDiscarded) { - orderedBuilder.Specification.AddInternal(ItemType.Order, orderExpression, (int)OrderType.ThenBy); + builder.Specification.AddInternal(ItemType.Order, keySelector, (int)OrderType.ThenBy); } else { Specification.IsChainDiscarded = true; } - return orderedBuilder; + return builder; } + /// + /// Adds a ThenBy clause to the specification. + /// + /// The type of the entity. + /// The ordered specification builder. + /// The key selector expression. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder ThenBy( - this IOrderedSpecificationBuilder orderedBuilder, - Expression> orderExpression) - => ThenBy(orderedBuilder, orderExpression, true); + this IOrderedSpecificationBuilder builder, + Expression> keySelector) + => ThenBy(builder, keySelector, true); + /// + /// Adds a ThenBy clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The ordered specification builder. + /// The key selector expression. + /// The condition to evaluate. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder ThenBy( - this IOrderedSpecificationBuilder orderedBuilder, - Expression> orderExpression, + this IOrderedSpecificationBuilder builder, + Expression> keySelector, bool condition) { if (condition && !Specification.IsChainDiscarded) { - orderedBuilder.Specification.AddInternal(ItemType.Order, orderExpression, (int)OrderType.ThenBy); + builder.Specification.AddInternal(ItemType.Order, keySelector, (int)OrderType.ThenBy); } else { Specification.IsChainDiscarded = true; } - return orderedBuilder; + return builder; } + /// + /// Adds a ThenByDescending clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The ordered specification builder. + /// The key selector expression. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder ThenByDescending( - this IOrderedSpecificationBuilder orderedBuilder, - Expression> orderExpression) - => ThenByDescending(orderedBuilder, orderExpression, true); + this IOrderedSpecificationBuilder builder, + Expression> keySelector) + => ThenByDescending(builder, keySelector, true); + /// + /// Adds a ThenByDescending clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The ordered specification builder. + /// The key selector expression. + /// The condition to evaluate. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder ThenByDescending( - this IOrderedSpecificationBuilder orderedBuilder, - Expression> orderExpression, + this IOrderedSpecificationBuilder builder, + Expression> keySelector, bool condition) { if (condition && !Specification.IsChainDiscarded) { - orderedBuilder.Specification.AddInternal(ItemType.Order, orderExpression, (int)OrderType.ThenByDescending); + builder.Specification.AddInternal(ItemType.Order, keySelector, (int)OrderType.ThenByDescending); } else { Specification.IsChainDiscarded = true; } - return orderedBuilder; + return builder; } + /// + /// Adds a ThenByDescending clause to the specification. + /// + /// The type of the entity. + /// The ordered specification builder. + /// The key selector expression. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder ThenByDescending( - this IOrderedSpecificationBuilder orderedBuilder, - Expression> orderExpression) - => ThenByDescending(orderedBuilder, orderExpression, true); + this IOrderedSpecificationBuilder builder, + Expression> keySelector) + => ThenByDescending(builder, keySelector, true); + /// + /// Adds a ThenByDescending clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The ordered specification builder. + /// The key selector expression. + /// The condition to evaluate. + /// The updated ordered specification builder. public static IOrderedSpecificationBuilder ThenByDescending( - this IOrderedSpecificationBuilder orderedBuilder, - Expression> orderExpression, + this IOrderedSpecificationBuilder builder, + Expression> keySelector, bool condition) { if (condition && !Specification.IsChainDiscarded) { - orderedBuilder.Specification.AddInternal(ItemType.Order, orderExpression, (int)OrderType.ThenByDescending); + builder.Specification.AddInternal(ItemType.Order, keySelector, (int)OrderType.ThenByDescending); } else { Specification.IsChainDiscarded = true; } - return orderedBuilder; + return builder; } } diff --git a/src/QuerySpecification/Builders/Builder_Paging.cs b/src/QuerySpecification/Builders/Builder_Paging.cs index c364b49..12c0834 100644 --- a/src/QuerySpecification/Builders/Builder_Paging.cs +++ b/src/QuerySpecification/Builders/Builder_Paging.cs @@ -2,11 +2,28 @@ public static partial class SpecificationBuilderExtensions { + /// + /// Sets the number of items to take in the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The number of items to take. + /// The updated specification builder. public static ISpecificationBuilder Take( this ISpecificationBuilder builder, int take) => Take(builder, take, true); + /// + /// Sets the number of items to take in the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The number of items to take. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder Take( this ISpecificationBuilder builder, int take, @@ -19,11 +36,26 @@ public static ISpecificationBuilder Take( return builder; } + /// + /// Sets the number of items to take in the specification. + /// + /// The type of the entity. + /// The specification builder. + /// The number of items to take. + /// The updated specification builder. public static ISpecificationBuilder Take( this ISpecificationBuilder builder, int take) => Take(builder, take, true); + /// + /// Sets the number of items to take in the specification if the condition is true. + /// + /// The type of the entity. + /// The specification builder. + /// The number of items to take. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder Take( this ISpecificationBuilder builder, int take, @@ -36,11 +68,28 @@ public static ISpecificationBuilder Take( return builder; } + /// + /// Sets the number of items to skip in the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The number of items to skip. + /// The updated specification builder. public static ISpecificationBuilder Skip( this ISpecificationBuilder builder, int skip) => Skip(builder, skip, true); + /// + /// Sets the number of items to skip in the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The number of items to skip. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder Skip( this ISpecificationBuilder builder, int skip, @@ -53,11 +102,26 @@ public static ISpecificationBuilder Skip( return builder; } + /// + /// Sets the number of items to skip in the specification. + /// + /// The type of the entity. + /// The specification builder. + /// The number of items to skip. + /// The updated specification builder. public static ISpecificationBuilder Skip( this ISpecificationBuilder builder, int skip) => Skip(builder, skip, true); + /// + /// Sets the number of items to skip in the specification if the condition is true. + /// + /// The type of the entity. + /// The specification builder. + /// The number of items to skip. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder Skip( this ISpecificationBuilder builder, int skip, diff --git a/src/QuerySpecification/Builders/Builder_Select.cs b/src/QuerySpecification/Builders/Builder_Select.cs index 7f087e8..13d791f 100644 --- a/src/QuerySpecification/Builders/Builder_Select.cs +++ b/src/QuerySpecification/Builders/Builder_Select.cs @@ -2,6 +2,13 @@ public static partial class SpecificationBuilderExtensions { + /// + /// Adds a Select clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The selector expression. public static void Select( this ISpecificationBuilder builder, Expression> selector) @@ -9,6 +16,13 @@ public static void Select( builder.Specification.AddOrUpdateInternal(ItemType.Select, selector, (int)SelectType.Select); } + /// + /// Adds a SelectMany clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The selector expression. public static void SelectMany( this ISpecificationBuilder builder, Expression>> selector) diff --git a/src/QuerySpecification/Builders/Builder_Where.cs b/src/QuerySpecification/Builders/Builder_Where.cs index b9ac430..a43e592 100644 --- a/src/QuerySpecification/Builders/Builder_Where.cs +++ b/src/QuerySpecification/Builders/Builder_Where.cs @@ -2,36 +2,68 @@ public static partial class SpecificationBuilderExtensions { + /// + /// Adds a Where clause to the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The predicate expression. + /// The updated specification builder. public static ISpecificationBuilder Where( this ISpecificationBuilder builder, - Expression> criteria) - => Where(builder, criteria, true); + Expression> predicate) + => Where(builder, predicate, true); + /// + /// Adds a Where clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The type of the result. + /// The specification builder. + /// The predicate expression. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder Where( this ISpecificationBuilder builder, - Expression> criteria, + Expression> predicate, bool condition) { if (condition) { - builder.Specification.AddInternal(ItemType.Where, criteria); + builder.Specification.AddInternal(ItemType.Where, predicate); } return builder; } + /// + /// Adds a Where clause to the specification. + /// + /// The type of the entity. + /// The specification builder. + /// The predicate expression. + /// The updated specification builder. public static ISpecificationBuilder Where( this ISpecificationBuilder builder, - Expression> criteria) - => Where(builder, criteria, true); + Expression> predicate) + => Where(builder, predicate, true); + /// + /// Adds a Where clause to the specification if the condition is true. + /// + /// The type of the entity. + /// The specification builder. + /// The predicate expression. + /// The condition to evaluate. + /// The updated specification builder. public static ISpecificationBuilder Where( this ISpecificationBuilder builder, - Expression> criteria, + Expression> predicate, bool condition) { if (condition) { - builder.Specification.AddInternal(ItemType.Where, criteria); + builder.Specification.AddInternal(ItemType.Where, predicate); } return builder; } diff --git a/src/QuerySpecification/Builders/IncludableSpecificationBuilder.cs b/src/QuerySpecification/Builders/IncludableSpecificationBuilder.cs index 25a1498..2ac3eb8 100644 --- a/src/QuerySpecification/Builders/IncludableSpecificationBuilder.cs +++ b/src/QuerySpecification/Builders/IncludableSpecificationBuilder.cs @@ -1,9 +1,20 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a specification builder that supports include operations. +/// +/// The type of the entity. +/// The type of the result. +/// The type of the property. public interface IIncludableSpecificationBuilder : ISpecificationBuilder where T : class { } +/// +/// Represents a specification builder that supports include operations. +/// +/// The type of the entity. +/// The type of the property. public interface IIncludableSpecificationBuilder : ISpecificationBuilder where T : class { } diff --git a/src/QuerySpecification/Builders/SpecificationBuilder.cs b/src/QuerySpecification/Builders/SpecificationBuilder.cs index 789df10..85c2bab 100644 --- a/src/QuerySpecification/Builders/SpecificationBuilder.cs +++ b/src/QuerySpecification/Builders/SpecificationBuilder.cs @@ -1,24 +1,74 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a specification builder that supports order operations. +/// +/// The type of the entity. +/// The type of the result. public interface IOrderedSpecificationBuilder : ISpecificationBuilder { } +/// +/// Represents a specification builder that supports order operations. +/// +/// The type of the entity. public interface IOrderedSpecificationBuilder : ISpecificationBuilder { } +/// +/// Represents a specification builder. +/// +/// The type of the entity. +/// The type of the result. public interface ISpecificationBuilder { internal Specification Specification { get; } + + /// + /// Adds an item to the specification. + /// + /// The type of the item. + /// The object to be stored in the item. + /// Thrown if value is null + /// Thrown if type is zero or negative. void Add(int type, object value); + + /// + /// Adds or updates an item in the specification. + /// + /// The type of the item. + /// The object to be stored in the item. + /// Thrown if value is null + /// Thrown if type is zero or negative. void AddOrUpdate(int type, object value); } +/// +/// Represents a specification builder. +/// +/// The type of the entity. public interface ISpecificationBuilder { internal Specification Specification { get; } + + /// + /// Adds an item to the specification. + /// + /// The type of the item. + /// The object to be stored in the item. + /// Thrown if value is null + /// Thrown if type is zero or negative. void Add(int type, object value); + + /// + /// Adds or updates an item in the specification. + /// + /// The type of the item. + /// The object to be stored in the item. + /// Thrown if value is null + /// Thrown if type is zero or negative. void AddOrUpdate(int type, object value); } diff --git a/src/QuerySpecification/Evaluators/IEvaluator.cs b/src/QuerySpecification/Evaluators/IEvaluator.cs index 9fa7ed4..46555ce 100644 --- a/src/QuerySpecification/Evaluators/IEvaluator.cs +++ b/src/QuerySpecification/Evaluators/IEvaluator.cs @@ -1,6 +1,16 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents an evaluator that processes a specification. +/// public interface IEvaluator { + /// + /// Evaluates the given specification on the provided queryable source. + /// + /// The type of the entity. + /// The queryable source. + /// The specification to evaluate. + /// The evaluated queryable source. IQueryable Evaluate(IQueryable source, Specification specification) where T : class; } diff --git a/src/QuerySpecification/Evaluators/IInMemoryEvaluator.cs b/src/QuerySpecification/Evaluators/IInMemoryEvaluator.cs index 39e1bb1..a481ee7 100644 --- a/src/QuerySpecification/Evaluators/IInMemoryEvaluator.cs +++ b/src/QuerySpecification/Evaluators/IInMemoryEvaluator.cs @@ -1,6 +1,16 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents an in-memory evaluator that processes a specification. +/// public interface IInMemoryEvaluator { + /// + /// Evaluates the given specification on the provided enumerable source. + /// + /// The type of the entity. + /// The enumerable source. + /// The specification to evaluate. + /// The evaluated enumerable source. IEnumerable Evaluate(IEnumerable source, Specification specification); } diff --git a/src/QuerySpecification/Evaluators/LikeMemoryEvaluator.cs b/src/QuerySpecification/Evaluators/LikeMemoryEvaluator.cs index dec2f85..9859d27 100644 --- a/src/QuerySpecification/Evaluators/LikeMemoryEvaluator.cs +++ b/src/QuerySpecification/Evaluators/LikeMemoryEvaluator.cs @@ -17,11 +17,18 @@ public IEnumerable Evaluate(IEnumerable source, Specification specif For source of 1000 items, the allocations are reduced from 257.872 bytes to only 64 bytes (the cost of the iterator instance). Refer to LikeInMemoryEvaluatorBenchmark results. */ +/// +/// Represents an in-memory evaluator for "Like" expressions. +/// public sealed class LikeMemoryEvaluator : IInMemoryEvaluator { - private LikeMemoryEvaluator() { } + /// + /// Gets the singleton instance of the class. + /// public static LikeMemoryEvaluator Instance = new(); + private LikeMemoryEvaluator() { } + /// public IEnumerable Evaluate(IEnumerable source, Specification specification) { var compiledItems = specification.GetCompiledItems(); diff --git a/src/QuerySpecification/Evaluators/OrderEvaluator.cs b/src/QuerySpecification/Evaluators/OrderEvaluator.cs index a10caaa..5351b0a 100644 --- a/src/QuerySpecification/Evaluators/OrderEvaluator.cs +++ b/src/QuerySpecification/Evaluators/OrderEvaluator.cs @@ -1,10 +1,17 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents an evaluator for order expressions. +/// public sealed class OrderEvaluator : IEvaluator, IInMemoryEvaluator { - private OrderEvaluator() { } + /// + /// Gets the singleton instance of the class. + /// public static OrderEvaluator Instance = new(); + private OrderEvaluator() { } + /// public IQueryable Evaluate(IQueryable source, Specification specification) where T : class { IOrderedQueryable? orderedQuery = null; @@ -40,6 +47,7 @@ public IQueryable Evaluate(IQueryable source, Specification specific return source; } + /// public IEnumerable Evaluate(IEnumerable source, Specification specification) { var compiledItems = specification.GetCompiledItems(); diff --git a/src/QuerySpecification/Evaluators/PaginationExtensions.cs b/src/QuerySpecification/Evaluators/PaginationExtensions.cs index 4b359e7..2fe9600 100644 --- a/src/QuerySpecification/Evaluators/PaginationExtensions.cs +++ b/src/QuerySpecification/Evaluators/PaginationExtensions.cs @@ -1,7 +1,18 @@ namespace Pozitron.QuerySpecification; +/// +/// Provides extension methods for applying pagination to queryable and enumerable sources. +/// public static class PaginationExtensions { + /// + /// Applies pagination to the queryable source based on the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The queryable source. + /// The specification containing pagination settings. + /// The paginated queryable source. public static IQueryable ApplyPaging(this IQueryable source, Specification specification) { var paging = specification.FirstOrDefault(ItemType.Paging); @@ -10,6 +21,14 @@ public static IQueryable ApplyPaging(this IQueryable + /// Applies pagination to the enumerable source based on the specification. + /// + /// The type of the entity. + /// The type of the result. + /// The enumerable source. + /// The specification containing pagination settings. + /// The paginated enumerable source. public static IEnumerable ApplyPaging(this IEnumerable source, Specification specification) { var paging = specification.FirstOrDefault(ItemType.Paging); @@ -18,9 +37,23 @@ public static IEnumerable ApplyPaging(this IEnumerable + /// Applies pagination to the queryable source based on the pagination settings. + /// + /// The type of the entity. + /// The queryable source. + /// The pagination settings. + /// The paginated queryable source. public static IQueryable ApplyPaging(this IQueryable source, Pagination pagination) => ApplyPaging(source, pagination.Skip, pagination.Take); + /// + /// Applies pagination to the queryable source based on the specification. + /// + /// The type of the entity. + /// The queryable source. + /// The specification containing pagination settings. + /// The paginated queryable source. public static IQueryable ApplyPaging(this IQueryable source, Specification specification) { var paging = specification.FirstOrDefault(ItemType.Paging); @@ -29,6 +62,13 @@ public static IQueryable ApplyPaging(this IQueryable source, Specificat return ApplyPaging(source, paging.Skip, paging.Take); } + /// + /// Applies pagination to the enumerable source based on the specification. + /// + /// The type of the entity. + /// The enumerable source. + /// The specification containing pagination settings. + /// The paginated enumerable source. public static IEnumerable ApplyPaging(this IEnumerable source, Specification specification) { var paging = specification.FirstOrDefault(ItemType.Paging); @@ -68,4 +108,3 @@ private static IEnumerable ApplyPaging(this IEnumerable source, int ski return source; } } - diff --git a/src/QuerySpecification/Evaluators/SpecificationInMemoryEvaluator.cs b/src/QuerySpecification/Evaluators/SpecificationInMemoryEvaluator.cs index 7ba633c..9c6b617 100644 --- a/src/QuerySpecification/Evaluators/SpecificationInMemoryEvaluator.cs +++ b/src/QuerySpecification/Evaluators/SpecificationInMemoryEvaluator.cs @@ -1,11 +1,23 @@ namespace Pozitron.QuerySpecification; +/// +/// Evaluates specifications in memory. +/// public class SpecificationInMemoryEvaluator { + /// + /// Gets the default instance of the class. + /// public static SpecificationInMemoryEvaluator Default = new(); + /// + /// Gets the list of in-memory evaluators. + /// protected List Evaluators { get; } + /// + /// Initializes a new instance of the class. + /// public SpecificationInMemoryEvaluator() { Evaluators = @@ -15,11 +27,25 @@ public SpecificationInMemoryEvaluator() LikeMemoryEvaluator.Instance, ]; } + + /// + /// Initializes a new instance of the class with the specified evaluators. + /// + /// The in-memory evaluators to use. public SpecificationInMemoryEvaluator(IEnumerable evaluators) { Evaluators = evaluators.ToList(); } + /// + /// Evaluates the given specification on the provided enumerable source and returns the result. + /// + /// The type of the entity. + /// The type of the result. + /// The enumerable source. + /// The specification to evaluate. + /// Whether to ignore paging settings (Take/Skip) defined in the specification. + /// The evaluated enumerable result. public virtual IEnumerable Evaluate( IEnumerable source, Specification specification, @@ -46,6 +72,14 @@ public virtual IEnumerable Evaluate( : result.ApplyPaging(specification); } + /// + /// Evaluates the given specification on the provided enumerable source and returns the result. + /// + /// The type of the entity. + /// The enumerable source. + /// The specification to evaluate. + /// Whether to ignore paging settings (Take/Skip) defined in the specification. + /// The evaluated enumerable result. public virtual IEnumerable Evaluate( IEnumerable source, Specification specification, diff --git a/src/QuerySpecification/Evaluators/WhereEvaluator.cs b/src/QuerySpecification/Evaluators/WhereEvaluator.cs index 04ce4e8..a162949 100644 --- a/src/QuerySpecification/Evaluators/WhereEvaluator.cs +++ b/src/QuerySpecification/Evaluators/WhereEvaluator.cs @@ -1,10 +1,17 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents an evaluator for where expressions. +/// public sealed class WhereEvaluator : IEvaluator, IInMemoryEvaluator { - private WhereEvaluator() { } + /// + /// Gets the singleton instance of the class. + /// public static WhereEvaluator Instance = new(); + private WhereEvaluator() { } + /// public IQueryable Evaluate(IQueryable source, Specification specification) where T : class { foreach (var item in specification.Items) @@ -18,6 +25,7 @@ public IQueryable Evaluate(IQueryable source, Specification specific return source; } + /// public IEnumerable Evaluate(IEnumerable source, Specification specification) { var compiledItems = specification.GetCompiledItems(); @@ -33,4 +41,3 @@ public IEnumerable Evaluate(IEnumerable source, Specification specif return source; } } - diff --git a/src/QuerySpecification/Exceptions/ConcurrentSelectorsException.cs b/src/QuerySpecification/Exceptions/ConcurrentSelectorsException.cs index 2bcd0df..1347f95 100644 --- a/src/QuerySpecification/Exceptions/ConcurrentSelectorsException.cs +++ b/src/QuerySpecification/Exceptions/ConcurrentSelectorsException.cs @@ -1,14 +1,24 @@ namespace Pozitron.QuerySpecification; +/// +/// Exception thrown when concurrent selectors are defined in the specification. +/// public class ConcurrentSelectorsException : Exception { private const string _message = "Concurrent specification selector transforms defined. Ensure only one of the Select() or SelectMany() transforms is used in the same specification!"; + /// + /// Initializes a new instance of the class. + /// public ConcurrentSelectorsException() : base(_message) { } + /// + /// Initializes a new instance of the class with a specified inner exception. + /// + /// The exception that is the cause of this exception. public ConcurrentSelectorsException(Exception innerException) : base(_message, innerException) { diff --git a/src/QuerySpecification/Exceptions/EntityNotFoundException.cs b/src/QuerySpecification/Exceptions/EntityNotFoundException.cs index 6cc5477..227fbe2 100644 --- a/src/QuerySpecification/Exceptions/EntityNotFoundException.cs +++ b/src/QuerySpecification/Exceptions/EntityNotFoundException.cs @@ -1,17 +1,32 @@ namespace Pozitron.QuerySpecification; +/// +/// Exception thrown when an entity is not found. +/// public class EntityNotFoundException : Exception { + /// + /// Initializes a new instance of the class. + /// public EntityNotFoundException() : base($"The queried entity was not found!") { } + /// + /// Initializes a new instance of the class with a specified entity name. + /// + /// The name of the entity that was not found. public EntityNotFoundException(string entityName) : base($"The queried entity: {entityName} was not found!") { } + /// + /// Initializes a new instance of the class with a specified entity name and inner exception. + /// + /// The name of the entity that was not found. + /// The exception that is the cause of this exception. public EntityNotFoundException(string entityName, Exception innerException) : base($"The queried entity: {entityName} was not found!", innerException) { diff --git a/src/QuerySpecification/Exceptions/InvalidLikePatternException.cs b/src/QuerySpecification/Exceptions/InvalidLikePatternException.cs index 70d071c..1e5d54c 100644 --- a/src/QuerySpecification/Exceptions/InvalidLikePatternException.cs +++ b/src/QuerySpecification/Exceptions/InvalidLikePatternException.cs @@ -1,14 +1,26 @@ namespace Pozitron.QuerySpecification; +/// +/// Exception thrown when an invalid like pattern is encountered. +/// public class InvalidLikePatternException : Exception { private const string _message = "Invalid like pattern: "; + /// + /// Initializes a new instance of the class with a specified pattern. + /// + /// The invalid like pattern. public InvalidLikePatternException(string pattern) : base($"{_message}{pattern}") { } + /// + /// Initializes a new instance of the class with a specified pattern and inner exception. + /// + /// The invalid like pattern. + /// The exception that is the cause of this exception. public InvalidLikePatternException(string pattern, Exception innerException) : base($"{_message}{pattern}", innerException) { diff --git a/src/QuerySpecification/Exceptions/SelectorNotFoundException.cs b/src/QuerySpecification/Exceptions/SelectorNotFoundException.cs index 97ee607..d559596 100644 --- a/src/QuerySpecification/Exceptions/SelectorNotFoundException.cs +++ b/src/QuerySpecification/Exceptions/SelectorNotFoundException.cs @@ -1,14 +1,24 @@ namespace Pozitron.QuerySpecification; +/// +/// Exception thrown when a selector is not found in the specification. +/// public class SelectorNotFoundException : Exception { private const string _message = "The specification must have a selector transform defined. Ensure either Select() or SelectMany() is used in the specification!"; + /// + /// Initializes a new instance of the class. + /// public SelectorNotFoundException() : base(_message) { } + /// + /// Initializes a new instance of the class with a specified inner exception. + /// + /// The exception that is the cause of this exception. public SelectorNotFoundException(Exception innerException) : base(_message, innerException) { diff --git a/src/QuerySpecification/Expressions/IncludeExpression.cs b/src/QuerySpecification/Expressions/IncludeExpression.cs index 638577a..260c271 100644 --- a/src/QuerySpecification/Expressions/IncludeExpression.cs +++ b/src/QuerySpecification/Expressions/IncludeExpression.cs @@ -2,11 +2,27 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents an include expression used in a specification. +/// +/// The type of the entity. public sealed class IncludeExpression { + /// + /// Gets the lambda expression for the include. + /// public LambdaExpression LambdaExpression { get; } + + /// + /// Gets the type of the include. + /// public IncludeType Type { get; } + /// + /// Initializes a new instance of the class. + /// + /// The lambda expression for the include. + /// The type of the include. public IncludeExpression(LambdaExpression expression, IncludeType type) { Debug.Assert(expression is not null); diff --git a/src/QuerySpecification/Expressions/IncludeType.cs b/src/QuerySpecification/Expressions/IncludeType.cs index 5ab1cdd..8b8c8ac 100644 --- a/src/QuerySpecification/Expressions/IncludeType.cs +++ b/src/QuerySpecification/Expressions/IncludeType.cs @@ -1,7 +1,17 @@ namespace Pozitron.QuerySpecification; +/// +/// Specifies the type of include operation in a specification. +/// public enum IncludeType { + /// + /// Represents an include operation. + /// Include = 1, + + /// + /// Represents a then include operation. + /// ThenInclude = 2 } diff --git a/src/QuerySpecification/Expressions/LikeExpression.cs b/src/QuerySpecification/Expressions/LikeExpression.cs index b57c41e..03bb43f 100644 --- a/src/QuerySpecification/Expressions/LikeExpression.cs +++ b/src/QuerySpecification/Expressions/LikeExpression.cs @@ -2,12 +2,33 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a like expression used in a specification. +/// +/// The type of the entity. public sealed class LikeExpression { + /// + /// Gets the key selector expression. + /// public Expression> KeySelector { get; } + + /// + /// Gets the pattern to match. + /// public string Pattern { get; } + + /// + /// Gets the group number. + /// public int Group { get; } + /// + /// Initializes a new instance of the class. + /// + /// The key selector expression. + /// The pattern to match. + /// The group number. public LikeExpression(Expression> keySelector, string pattern, int group = 1) { Debug.Assert(keySelector is not null); @@ -18,12 +39,33 @@ public LikeExpression(Expression> keySelector, string pattern, } } +/// +/// Represents a compiled like expression used in a specification. +/// +/// The type of the entity. public sealed class LikeExpressionCompiled { + /// + /// Gets the compiled key selector function. + /// public Func KeySelector { get; } + + /// + /// Gets the pattern to match. + /// public string Pattern { get; } + + /// + /// Gets the group number. + /// public int Group { get; } + /// + /// Initializes a new instance of the class. + /// + /// The compiled key selector function. + /// The pattern to match. + /// The group number. public LikeExpressionCompiled(Func keySelector, string pattern, int group = 1) { Debug.Assert(keySelector is not null); diff --git a/src/QuerySpecification/Expressions/OrderExpression.cs b/src/QuerySpecification/Expressions/OrderExpression.cs index e1990f1..d5a43b2 100644 --- a/src/QuerySpecification/Expressions/OrderExpression.cs +++ b/src/QuerySpecification/Expressions/OrderExpression.cs @@ -2,11 +2,27 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents an order expression used in a specification. +/// +/// The type of the entity. public sealed class OrderExpression { + /// + /// Gets the key selector expression. + /// public Expression> KeySelector { get; } + + /// + /// Gets the type of the order. + /// public OrderType Type { get; } + /// + /// Initializes a new instance of the class. + /// + /// The key selector expression. + /// The type of the order. public OrderExpression(Expression> keySelector, OrderType type) { Debug.Assert(keySelector is not null); @@ -15,11 +31,27 @@ public OrderExpression(Expression> keySelector, OrderType type) } } +/// +/// Represents a compiled order expression used in a specification. +/// +/// The type of the entity. public sealed class OrderExpressionCompiled { + /// + /// Gets the compiled key selector function. + /// public Func KeySelector { get; } + + /// + /// Gets the type of the order. + /// public OrderType Type { get; } + /// + /// Initializes a new instance of the class. + /// + /// The compiled key selector function. + /// The type of the order. public OrderExpressionCompiled(Func keySelector, OrderType type) { Debug.Assert(keySelector is not null); diff --git a/src/QuerySpecification/Expressions/OrderType.cs b/src/QuerySpecification/Expressions/OrderType.cs index 9b1df79..7d2e016 100644 --- a/src/QuerySpecification/Expressions/OrderType.cs +++ b/src/QuerySpecification/Expressions/OrderType.cs @@ -1,9 +1,27 @@ namespace Pozitron.QuerySpecification; +/// +/// Specifies the type of order operation in a specification. +/// public enum OrderType { + /// + /// Represents an order by operation. + /// OrderBy = 1, + + /// + /// Represents an order by descending operation. + /// OrderByDescending = 2, + + /// + /// Represents a then by operation. + /// ThenBy = 3, + + /// + /// Represents a then by descending operation. + /// ThenByDescending = 4 } diff --git a/src/QuerySpecification/Expressions/SelectType.cs b/src/QuerySpecification/Expressions/SelectType.cs index 451323d..04eb139 100644 --- a/src/QuerySpecification/Expressions/SelectType.cs +++ b/src/QuerySpecification/Expressions/SelectType.cs @@ -1,7 +1,17 @@ namespace Pozitron.QuerySpecification; +/// +/// Specifies the type of select operation in a specification. +/// public enum SelectType { + /// + /// Represents a select operation. + /// Select = 1, + + /// + /// Represents a select many operation. + /// SelectMany = 2 } diff --git a/src/QuerySpecification/Expressions/WhereExpression.cs b/src/QuerySpecification/Expressions/WhereExpression.cs index 8a84050..9b2c012 100644 --- a/src/QuerySpecification/Expressions/WhereExpression.cs +++ b/src/QuerySpecification/Expressions/WhereExpression.cs @@ -2,10 +2,21 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a where expression used in a specification. +/// +/// The type of the entity. public sealed class WhereExpression { + /// + /// Gets the filter expression. + /// public Expression> Filter { get; } + /// + /// Initializes a new instance of the class. + /// + /// The filter expression. public WhereExpression(Expression> filter) { Debug.Assert(filter is not null); @@ -13,10 +24,21 @@ public WhereExpression(Expression> filter) } } +/// +/// Represents a compiled where expression used in a specification. +/// +/// The type of the entity. public sealed class WhereExpressionCompiled { + /// + /// Gets the compiled filter function. + /// public Func Filter { get; } + /// + /// Initializes a new instance of the class. + /// + /// The compiled filter function. public WhereExpressionCompiled(Func filter) { Debug.Assert(filter is not null); diff --git a/src/QuerySpecification/IProjectionRepository.cs b/src/QuerySpecification/IProjectionRepository.cs index 3f49dbe..2d14563 100644 --- a/src/QuerySpecification/IProjectionRepository.cs +++ b/src/QuerySpecification/IProjectionRepository.cs @@ -1,9 +1,51 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a repository for projecting entities to different result types. +/// +/// The type of the entity. public interface IProjectionRepository where T : class { + /// + /// Projects the first entity that matches the specification to a result. It throws an exception if no entity is found. + /// It ignores the selector in the specification, and projects the entity to the result type using the Map method. + /// + /// The type of the result. + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the projected result. + /// Task ProjectToFirstAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Projects the first entity that matches the specification to a result or null if no entity is found. + /// It ignores the selector in the specification, and projects the entity to the result type using the Map method. + /// + /// The type of the result. + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the projected result or null if no entity is found. Task ProjectToFirstOrDefaultAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Projects the entities that match the specification to a list of results. + /// It ignores the selector in the specification. It projects the entities to the result type using the Map method. + /// + /// The type of the result. + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the list of projected results. Task> ProjectToListAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Projects the entities that match the specification to a paged list of results. + /// It ignores the selector in the specification, and projects the entities to the result type using the Map method. + /// It ignores the paging filter in the specification, and applies pagination based on the provided paging filter. + /// + /// The type of the result. + /// The specification to evaluate. + /// The paging filter. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the paged list of projected results. Task> ProjectToListAsync(Specification specification, PagingFilter filter, CancellationToken cancellationToken = default); } diff --git a/src/QuerySpecification/IReadRepositoryBase.cs b/src/QuerySpecification/IReadRepositoryBase.cs index c962e8e..ecab0c4 100644 --- a/src/QuerySpecification/IReadRepositoryBase.cs +++ b/src/QuerySpecification/IReadRepositoryBase.cs @@ -1,22 +1,153 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a read-only repository for accessing entities. +/// +/// The type of the entity. public interface IReadRepositoryBase where T : class { + /// + /// Finds an entity by its identifier. + /// + /// The type of the identifier. + /// The identifier of the entity. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the entity if found; otherwise, null. ValueTask FindAsync(TId id, CancellationToken cancellationToken = default) where TId : notnull; + + /// + /// Gets the first entity that matches the specification. It throws an exception if no entity is found. + /// + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the first entity that matches the specification. + /// Task FirstAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Gets the first entity that matches the specification and projects it to a result. It throws an exception if no entity is found. + /// + /// The type of the result. + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the first entity that matches the specification and is projected to a result. + /// Task FirstAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Gets the first entity that matches the specification or null if no entity is found. + /// + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the first entity that matches the specification or null if no entity is found. Task FirstOrDefaultAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Gets the first entity that matches the specification and projects it to a result or null if no entity is found. + /// + /// The type of the result. + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the first entity that matches the specification and is projected to a result or null if no entity is found. Task FirstOrDefaultAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Gets the single entity that matches the specification or null if no entity is found. It throws an exception if there is not exactly one element in the sequence. + /// + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the single entity that matches the specification or null if no entity is found. Task SingleOrDefaultAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Gets the single entity that matches the specification and projects it to a result or null if no entity is found. It throws an exception if there is not exactly one element in the sequence. + /// + /// The type of the result. + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the single entity that matches the specification and is projected to a result or null if no entity is found. Task SingleOrDefaultAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Gets a list of all entities. + /// + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the list of all entities. Task> ListAsync(CancellationToken cancellationToken = default); + + /// + /// Gets a list of entities that match the specification. + /// + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the list of entities that match the specification. Task> ListAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Gets a list of entities that match the specification and projects them to a result. + /// + /// The type of the result. + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the list of entities that match the specification and are projected to a result. Task> ListAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Gets the count of all entities. + /// + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the count of all entities. Task CountAsync(CancellationToken cancellationToken = default); + + /// + /// Gets the count of entities that match the specification. + /// It ignores the skip and take values of the specification. + /// + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the count of entities that match the specification. Task CountAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Gets the count of entities that match the specification and projects them to a result. + /// It ignores the skip and take values of the specification. + /// + /// The type of the result. + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the count of entities that match the specification and are projected to a result. Task CountAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Determines whether any entities exist. + /// It ignores the skip and take values of the specification. + /// + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains true if any entities exist; otherwise, false. Task AnyAsync(CancellationToken cancellationToken = default); + + /// + /// Determines whether any entities match the specification. + /// It ignores the skip and take values of the specification. + /// + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains true if any entities match the specification; otherwise, false. Task AnyAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Determines whether any entities match the specification and projects them to a result. + /// + /// The type of the result. + /// The specification to evaluate. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains true if any entities match the specification and are projected to a result; otherwise, false. Task AnyAsync(Specification specification, CancellationToken cancellationToken = default); + + /// + /// Returns an asynchronous enumerable of entities that match the specification. + /// + /// The specification to evaluate. + /// An asynchronous enumerable of entities that match the specification. IAsyncEnumerable AsAsyncEnumerable(Specification specification); } diff --git a/src/QuerySpecification/IRepositoryBase.cs b/src/QuerySpecification/IRepositoryBase.cs index f5e7836..ccae248 100644 --- a/src/QuerySpecification/IRepositoryBase.cs +++ b/src/QuerySpecification/IRepositoryBase.cs @@ -1,11 +1,55 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a repository for accessing and modifying entities. +/// +/// The type of the entity. public interface IRepositoryBase : IReadRepositoryBase where T : class { + /// + /// Adds a new entity to the repository. + /// + /// The entity to add. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the added entity. Task AddAsync(T entity, CancellationToken cancellationToken = default); + + /// + /// Adds a range of new entities to the repository. + /// + /// The entities to add. + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the added entities. Task> AddRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default); + + /// + /// Updates an existing entity in the repository. + /// + /// The entity to update. + /// The cancellation token. + /// A task that represents the asynchronous operation. Task UpdateAsync(T entity, CancellationToken cancellationToken = default); + + /// + /// Deletes an existing entity from the repository. + /// + /// The entity to delete. + /// The cancellation token. + /// A task that represents the asynchronous operation. Task DeleteAsync(T entity, CancellationToken cancellationToken = default); + + /// + /// Deletes a range of existing entities from the repository. + /// + /// The entities to delete. + /// The cancellation token. + /// A task that represents the asynchronous operation. Task DeleteRangeAsync(IEnumerable entities, CancellationToken cancellationToken = default); + + /// + /// Saves all changes made in the repository. + /// + /// The cancellation token. + /// A task that represents the asynchronous operation. The task result contains the number of state entries written to the database. Task SaveChangesAsync(CancellationToken cancellationToken = default); } diff --git a/src/QuerySpecification/Paging/PagedResult.cs b/src/QuerySpecification/Paging/PagedResult.cs index d7e383b..164c329 100644 --- a/src/QuerySpecification/Paging/PagedResult.cs +++ b/src/QuerySpecification/Paging/PagedResult.cs @@ -1,10 +1,26 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a paged result with data and pagination information. +/// +/// The type of the data. public record PagedResult { + /// + /// Gets the pagination information. + /// public Pagination Pagination { get; } + + /// + /// Gets the data. + /// public List Data { get; } + /// + /// Initializes a new instance of the class. + /// + /// The data. + /// The pagination information. public PagedResult(List data, Pagination pagination) { Data = data; diff --git a/src/QuerySpecification/Paging/Pagination.cs b/src/QuerySpecification/Paging/Pagination.cs index 1067453..83e445f 100644 --- a/src/QuerySpecification/Paging/Pagination.cs +++ b/src/QuerySpecification/Paging/Pagination.cs @@ -3,24 +3,76 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents pagination information. +/// public record Pagination { private readonly PaginationSettings _paginationSettings; + /// + /// Gets the total number of items. + /// public int TotalItems { get; } + + /// + /// Gets the total number of pages. + /// public int TotalPages { get; } + + /// + /// Gets the page size. + /// public int PageSize { get; } + + /// + /// Gets the current page number. + /// public int Page { get; } + + /// + /// Gets the start item number. + /// public int StartItem { get; } + + /// + /// Gets the end item number. + /// public int EndItem { get; } + + /// + /// Gets a value indicating whether there is a previous page. + /// public bool HasPrevious { get; } + + /// + /// Gets a value indicating whether there is a next page. + /// public bool HasNext { get; } + /// + /// Gets the number of items to take. + /// [JsonIgnore] public int Take { get; } + + /// + /// Gets the number of items to skip. + /// [JsonIgnore] public int Skip { get; } + /// + /// Initializes a new instance of the class. + /// + /// The total number of items. + /// The total number of pages. + /// The page size. + /// The current page number. + /// The start item number. + /// The end item number. + /// A value indicating whether there is a previous page. + /// A value indicating whether there is a next page. [JsonConstructor] public Pagination(int totalItems, int totalPages, int pageSize, int page, int startItem, int endItem, bool hasPrevious, bool hasNext) { @@ -35,23 +87,50 @@ public Pagination(int totalItems, int totalPages, int pageSize, int page, int st HasNext = hasNext; } + /// + /// Gets an pagination instance with zero items. + /// public static Pagination Empty { get; } = new Pagination(PaginationSettings.Default, 0, null, null); + /// + /// Initializes a new instance of the class with the specified items count and filter. + /// + /// The total number of items. + /// The paging filter. public Pagination(int itemsCount, PagingFilter filter) : this(PaginationSettings.Default, itemsCount, filter.PageSize, filter.Page) { } + /// + /// Initializes a new instance of the class with the specified items count, page size, and page number. + /// + /// The total number of items. + /// The page size. + /// The current page number. public Pagination(int itemsCount, int? pageSize, int? page) : this(PaginationSettings.Default, itemsCount, pageSize, page) { } + /// + /// Initializes a new instance of the class with the specified pagination settings, items count, and filter. + /// + /// The pagination settings. + /// The total number of items. + /// The paging filter. public Pagination(PaginationSettings paginationSettings, int itemsCount, PagingFilter filter) : this(paginationSettings, itemsCount, filter.PageSize, filter.Page) { } + /// + /// Initializes a new instance of the class with the specified pagination settings, items count, page size, and page number. + /// + /// The pagination settings. + /// The total number of items. + /// The page size. + /// The current page number. public Pagination(PaginationSettings paginationSettings, int itemsCount, int? pageSize, int? page) { _paginationSettings = paginationSettings; diff --git a/src/QuerySpecification/Paging/PaginationSettings.cs b/src/QuerySpecification/Paging/PaginationSettings.cs index 79a3229..1c311a6 100644 --- a/src/QuerySpecification/Paging/PaginationSettings.cs +++ b/src/QuerySpecification/Paging/PaginationSettings.cs @@ -1,14 +1,37 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents pagination settings. +/// public record PaginationSettings { + /// + /// Gets the default page number. + /// public int DefaultPage { get; } = 1; + + /// + /// Gets the default page size. + /// public int DefaultPageSize { get; } = 10; + + /// + /// Gets the default page size limit. + /// public int DefaultPageSizeLimit { get; } = 50; + /// + /// Gets the default pagination settings. + /// public static PaginationSettings Default { get; } = new(); + private PaginationSettings() { } + /// + /// Initializes a new instance of the class with the specified default page size and page size limit. + /// + /// The default page size. + /// The default page size limit. public PaginationSettings(int defaultPageSize, int defaultPageSizeLimit) { DefaultPageSize = defaultPageSize; diff --git a/src/QuerySpecification/Paging/PagingFilter.cs b/src/QuerySpecification/Paging/PagingFilter.cs index d9f2e6b..7c2da92 100644 --- a/src/QuerySpecification/Paging/PagingFilter.cs +++ b/src/QuerySpecification/Paging/PagingFilter.cs @@ -1,7 +1,17 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a filter for paging. +/// public record PagingFilter { + /// + /// Gets or sets the page number. + /// public int? Page { get; init; } + + /// + /// Gets or sets the page size. + /// public int? PageSize { get; init; } } diff --git a/src/QuerySpecification/Specification.cs b/src/QuerySpecification/Specification.cs index 7e9a8c2..5040a3f 100644 --- a/src/QuerySpecification/Specification.cs +++ b/src/QuerySpecification/Specification.cs @@ -2,26 +2,59 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a specification with a result type. +/// +/// The type of the entity. +/// The type of the result. public class Specification : Specification { + /// + /// Initializes a new instance of the class. + /// public Specification() : base() { } + + /// + /// Initializes a new instance of the class with the specified initial capacity. + /// + /// The initial capacity of the specification. public Specification(int initialCapacity) : base(initialCapacity) { } + /// + /// Evaluates the given entities according to the specification and returns the result. + /// + /// The entities to evaluate. + /// Whether to ignore paging settings (Take/Skip) defined in the specification. + /// The evaluated result. public new virtual IEnumerable Evaluate(IEnumerable entities, bool ignorePaging = false) { var evaluator = Evaluator; return evaluator.Evaluate(entities, this, ignorePaging); } + /// + /// Gets the specification builder. + /// public new ISpecificationBuilder Query => new SpecificationBuilder(this); + /// + /// Gets the Select expression. + /// public Expression>? Selector => FirstOrDefault>>(ItemType.Select); + + /// + /// Gets the SelectMany expression. + /// public Expression>>? SelectorMany => FirstOrDefault>>>(ItemType.Select); } +/// +/// Represents a specification. +/// +/// The type of the entity. public partial class Specification { - private protected SpecItem[]? _items; + private SpecItem[]? _items; // It is utilized only during the building stage for the builder chains. Once the state is built, we don't care about it anymore. // The initial value is not important since the value is always initialized by the root of the chain. Therefore, we don't need ThreadLocal (it's more expensive). @@ -29,71 +62,162 @@ public partial class Specification [ThreadStatic] internal static bool IsChainDiscarded; + /// + /// Initializes a new instance of the class. + /// public Specification() { } + + /// + /// Initializes a new instance of the class with the specified initial capacity. + /// + /// The initial capacity of the specification. public Specification(int initialCapacity) { _items = new SpecItem[initialCapacity]; } + /// + /// Evaluates the given entities according to the specification and returns the result. + /// + /// The entities to evaluate. + /// Whether to ignore paging settings (Take/Skip) defined in the specification. + /// The evaluated result. public virtual IEnumerable Evaluate(IEnumerable entities, bool ignorePaging = false) { var evaluator = Evaluator; return evaluator.Evaluate(entities, this, ignorePaging); } + + /// + /// Determines whether the specified entity satisfies the specification. + /// + /// The entity to evaluate. + /// true if the entity satisfies the specification; otherwise, false. public virtual bool IsSatisfiedBy(T entity) { var validator = Validator; return validator.IsValid(entity, this); } + /// + /// Gets the evaluator. + /// protected virtual SpecificationInMemoryEvaluator Evaluator => SpecificationInMemoryEvaluator.Default; + + /// + /// Gets the validator. + /// protected virtual SpecificationValidator Validator => SpecificationValidator.Default; + /// + /// Gets the specification builder. + /// public ISpecificationBuilder Query => new SpecificationBuilder(this); + /// + /// Gets a value indicating whether the specification is empty. + /// [MemberNotNullWhen(false, nameof(_items))] public bool IsEmpty => _items is null; + /// + /// Gets the compiled where expressions. + /// public IEnumerable> WhereExpressionsCompiled => _items is null ? Enumerable.Empty>() : new SpecSelectIterator, WhereExpressionCompiled>(GetCompiledItems(), ItemType.Where, (x, bag) => new(x)); + /// + /// Gets the compiled order expressions. + /// public IEnumerable> OrderExpressionsCompiled => _items is null ? Enumerable.Empty>() : new SpecSelectIterator, OrderExpressionCompiled>(GetCompiledItems(), ItemType.Order, (x, bag) => new(x, (OrderType)bag)); + /// + /// Gets the compiled like expressions. + /// public IEnumerable> LikeExpressionsCompiled => _items is null ? Enumerable.Empty>() : new SpecSelectIterator, LikeExpressionCompiled>(GetCompiledItems(), ItemType.Like, (x, bag) => new(x.KeySelector, x.Pattern, bag)); + /// + /// Gets the where expressions. + /// public IEnumerable> WhereExpressions => _items is null ? Enumerable.Empty>() : new SpecSelectIterator>, WhereExpression>(_items, ItemType.Where, (x, bag) => new WhereExpression(x)); + /// + /// Gets the include expressions. + /// public IEnumerable> IncludeExpressions => _items is null ? Enumerable.Empty>() : new SpecSelectIterator>(_items, ItemType.Include, (x, bag) => new IncludeExpression(x, (IncludeType)bag)); + /// + /// Gets the order expressions. + /// public IEnumerable> OrderExpressions => _items is null ? Enumerable.Empty>() : new SpecSelectIterator>, OrderExpression>(_items, ItemType.Order, (x, bag) => new OrderExpression(x, (OrderType)bag)); + /// + /// Gets the like expressions. + /// public IEnumerable> LikeExpressions => _items is null ? Enumerable.Empty>() : new SpecSelectIterator, LikeExpression>(_items, ItemType.Like, (x, bag) => new LikeExpression(x.KeySelector, x.Pattern, bag)); + /// + /// Gets the include strings. + /// public IEnumerable IncludeStrings => _items is null ? Enumerable.Empty() : new SpecSelectIterator(_items, ItemType.IncludeString, (x, bag) => x); + /// + /// Gets the number of items to take. + /// public int Take => FirstOrDefault(ItemType.Paging)?.Take ?? -1; + + /// + /// Gets the number of items to skip. + /// public int Skip => FirstOrDefault(ItemType.Paging)?.Skip ?? -1; + + /// + /// Gets a value indicating whether IgnoreQueryFilters is applied. + /// public bool IgnoreQueryFilters => GetFlag(SpecFlags.IgnoreQueryFilters); + + /// + /// Gets a value indicating whether AsSplitQuery is applied. + /// public bool AsSplitQuery => GetFlag(SpecFlags.AsSplitQuery); + + /// + /// Gets a value indicating whether AsNoTracking is applied. + /// public bool AsNoTracking => GetFlag(SpecFlags.AsNoTracking); + + /// + /// Gets a value indicating whether AsNoTrackingWithIdentityResolution is applied. + /// public bool AsNoTrackingWithIdentityResolution => GetFlag(SpecFlags.AsNoTrackingWithIdentityResolution); + + /// + /// Gets a value indicating whether AsTracking is applied. + /// public bool AsTracking => GetFlag(SpecFlags.AsTracking); + /// + /// Adds an item to the specification. + /// + /// The type of the item. It must be a positive number. + /// The object to be stored in the item. + /// If value is null + /// If type is zero or negative. public void Add(int type, object value) { ArgumentNullException.ThrowIfNull(value); @@ -101,6 +225,14 @@ public void Add(int type, object value) AddInternal(type, value); } + + /// + /// Adds or updates an item in the specification. + /// + /// The type of the item. + /// The object to be stored in the item. + /// Thrown if value is null + /// Thrown if type is zero or negative. public void AddOrUpdate(int type, object value) { ArgumentNullException.ThrowIfNull(value); @@ -108,6 +240,13 @@ public void AddOrUpdate(int type, object value) AddOrUpdateInternal(type, value); } + + /// + /// Gets the first item of the specified type or the default value if no item is found. + /// + /// The type of the object stored in the item. + /// The type of the item. + /// The first item of the specified type or the default value if no item is found. public TObject? FirstOrDefault(int type) { if (IsEmpty) return default; @@ -121,6 +260,14 @@ public void AddOrUpdate(int type, object value) } return default; } + + /// + /// Gets the first item of the specified type. + /// + /// The type of the object stored in the item. + /// The type of the item. + /// The first item of the specified type. + /// Thrown if no matching item is found. public TObject First(int type) { if (IsEmpty) throw new InvalidOperationException("Specification contains no items"); @@ -134,6 +281,13 @@ public TObject First(int type) } throw new InvalidOperationException("Specification contains no matching item"); } + + /// + /// Gets all items of the specified type. + /// + /// The type of the object stored in the item. + /// The type of the items. + /// An enumerable of items of the specified type. public IEnumerable OfType(int type) => _items is null ? Enumerable.Empty() : new SpecIterator(_items, type); diff --git a/src/QuerySpecification/Validators/IValidator.cs b/src/QuerySpecification/Validators/IValidator.cs index 702a34e..3f51f0a 100644 --- a/src/QuerySpecification/Validators/IValidator.cs +++ b/src/QuerySpecification/Validators/IValidator.cs @@ -1,6 +1,16 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a validator for specifications. +/// public interface IValidator { + /// + /// Determines whether the specified entity is valid according to the given specification. + /// + /// The type of the entity. + /// The entity to validate. + /// The specification to evaluate. + /// true if the entity is valid; otherwise, false. bool IsValid(T entity, Specification specification); } diff --git a/src/QuerySpecification/Validators/LikeValidator.cs b/src/QuerySpecification/Validators/LikeValidator.cs index 387c6ae..c86caba 100644 --- a/src/QuerySpecification/Validators/LikeValidator.cs +++ b/src/QuerySpecification/Validators/LikeValidator.cs @@ -15,11 +15,18 @@ public bool IsValid(T entity, Specification specification) For 1000 entities, the allocations are reduced from 651.160 bytes to ZERO bytes. Refer to LikeValidatorBenchmark results. */ +/// +/// Represents a validator for "like" expressions. +/// public sealed class LikeValidator : IValidator { - private LikeValidator() { } + /// + /// Gets the singleton instance of the class. + /// public static LikeValidator Instance = new(); + private LikeValidator() { } + /// public bool IsValid(T entity, Specification specification) { var compiledItems = specification.GetCompiledItems(); diff --git a/src/QuerySpecification/Validators/SpecificationValidator.cs b/src/QuerySpecification/Validators/SpecificationValidator.cs index a9fecb7..e090052 100644 --- a/src/QuerySpecification/Validators/SpecificationValidator.cs +++ b/src/QuerySpecification/Validators/SpecificationValidator.cs @@ -1,11 +1,23 @@ namespace Pozitron.QuerySpecification; +/// +/// Validates specifications. +/// public class SpecificationValidator { + /// + /// Gets the default instance of the class. + /// public static SpecificationValidator Default = new(); + /// + /// Gets the list of validators. + /// protected List Validators { get; } + /// + /// Initializes a new instance of the class. + /// public SpecificationValidator() { Validators = @@ -14,11 +26,23 @@ public SpecificationValidator() LikeValidator.Instance ]; } + + /// + /// Initializes a new instance of the class with the specified validators. + /// + /// The validators to use. public SpecificationValidator(IEnumerable validators) { Validators = validators.ToList(); } + /// + /// Determines whether the specified entity is valid according to the given specification. + /// + /// The type of the entity. + /// The entity to validate. + /// The specification to evaluate. + /// true if the entity is valid; otherwise, false. public virtual bool IsValid(T entity, Specification specification) { if (specification.IsEmpty) return true; diff --git a/src/QuerySpecification/Validators/WhereValidator.cs b/src/QuerySpecification/Validators/WhereValidator.cs index fc40d75..9c76041 100644 --- a/src/QuerySpecification/Validators/WhereValidator.cs +++ b/src/QuerySpecification/Validators/WhereValidator.cs @@ -1,10 +1,17 @@ namespace Pozitron.QuerySpecification; +/// +/// Represents a validator for where expressions. +/// public sealed class WhereValidator : IValidator { - private WhereValidator() { } + /// + /// Gets the singleton instance of the class. + /// public static WhereValidator Instance = new(); + private WhereValidator() { } + /// public bool IsValid(T entity, Specification specification) { var compiledItems = specification.GetCompiledItems(); diff --git a/tests/QuerySpecification.Benchmarks/GloblUsings.cs b/tests/QuerySpecification.Benchmarks/GloblUsings.cs index 76cd24e..6285017 100644 --- a/tests/QuerySpecification.Benchmarks/GloblUsings.cs +++ b/tests/QuerySpecification.Benchmarks/GloblUsings.cs @@ -1,5 +1,5 @@ global using BenchmarkDotNet.Attributes; +global using BenchmarkDotNet.Running; global using Microsoft.EntityFrameworkCore; global using Pozitron.QuerySpecification; -global using BenchmarkDotNet.Running; global using QuerySpecification.Benchmarks; diff --git a/tests/QuerySpecification.EntityFrameworkCore.Tests/Evaluators/SpecificationEvaluatorTests.cs b/tests/QuerySpecification.EntityFrameworkCore.Tests/Evaluators/SpecificationEvaluatorTests.cs index da63be5..4a1c3e6 100644 --- a/tests/QuerySpecification.EntityFrameworkCore.Tests/Evaluators/SpecificationEvaluatorTests.cs +++ b/tests/QuerySpecification.EntityFrameworkCore.Tests/Evaluators/SpecificationEvaluatorTests.cs @@ -35,21 +35,6 @@ public void ThrowsSelectorNotFoundException_GivenNoSelector() sut.Should().Throw(); } - // TODO: We should allow overwriting. Think about this. [fatii, 26/10/2024] - //[Fact] - //public void ThrowsConcurrentSelectorsException_GivenBothSelectAndSelectMany() - //{ - // var spec = new Specification(); - // spec.Query - // .Select(x => x.Name); - // spec.Query - // .SelectMany(x => x.Products.Select(x => x.Name)); - - // var sut = () => _evaluator.Evaluate(DbContext.Stores, spec); - - // sut.Should().Throw(); - //} - [Fact] public void GivenEmptySpec() { diff --git a/tests/QuerySpecification.Tests/Evaluators/SpecificationInMemoryEvaluatorTests.cs b/tests/QuerySpecification.Tests/Evaluators/SpecificationInMemoryEvaluatorTests.cs index 8074456..4accc1b 100644 --- a/tests/QuerySpecification.Tests/Evaluators/SpecificationInMemoryEvaluatorTests.cs +++ b/tests/QuerySpecification.Tests/Evaluators/SpecificationInMemoryEvaluatorTests.cs @@ -35,21 +35,6 @@ public void Evaluate_ThrowsSelectorNotFoundException_GivenNoSelector() sut.Should().Throw(); } - // TODO: We should allow overwriting. Think about this. [fatii, 26/10/2024] - //[Fact] - //public void Evaluate_ThrowsConcurrentSelectorsException_GivenBothSelectAndSelectMany() - //{ - // var spec = new Specification(); - // spec.Query - // .Select(x => x.FirstName); - // spec.Query - // .SelectMany(x => x.Emails); - - // var sut = () => _evaluator.Evaluate([], spec); - - // sut.Should().Throw(); - //} - [Fact] public void Evaluate_Filters_GivenSpec() {