-
-
Notifications
You must be signed in to change notification settings - Fork 12
Service Query data
The following service interface is used for querying data:
public interface IModernQueryService<TEntityDto, TEntityDbo, in TId>
where TEntityDto : class
where TEntityDbo : class
where TId : IEquatable<TId>
TEntityDto
is a type of entity returned from the service
TEntityDbo
is a type of entity contained in the repository
TId
is a type of the entity's identifier (mainly primary key)
IModernQueryService<TEntityDto, TEntityDbo, TId>
has the following methods to get entity by id:
/// <summary>
/// Returns an entity with the given <paramref name="id"/>
/// </summary>
/// <param name="id">The entity id</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
/// <exception cref="ArgumentNullException">Thrown if provided id is null</exception>
/// <exception cref="EntityNotFoundException">Thrown if an entity does is not found</exception>
/// <exception cref="InternalErrorException">If a service internal error occurred</exception>
/// <returns>The entity</returns>
Task<TEntityDto> GetByIdAsync(TId id, CancellationToken cancellationToken = default);
/// <summary>
/// Tries to return an entity with the given <paramref name="id"/>; otherwise, <see langword="null"/>
/// </summary>
/// <remarks>
/// Method does not throw exception if entity is not found
/// </remarks>
/// <param name="id">The entity id</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
/// <exception cref="ArgumentNullException">Thrown if provided id is null</exception>
/// <exception cref="InternalErrorException">Thrown if an error occurred while retrieving entities</exception>
/// <returns>The entity</returns>
Task<TEntityDto?> TryGetByIdAsync(TId id, CancellationToken cancellationToken = default);
The main difference between them is that GetByIdAsync
method throws EntityNotFoundException
when entity is not found in the data store, while TryGetByIdAsync
returns null
when entity is not found.
Example:
var entity = await service.GetByIdAsync(1);
var nullableEntity = await service.TryGetByIdAsync(1);
IModernQueryService<TEntityDto, TEntityDbo, TId>
has the following method to get all entities:
/// <summary>
/// Returns all entities.<br/>
/// IMPORTANT: there can be performance issues when retrieving large amount of entities
/// </summary>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
/// <exception cref="InternalErrorException">Thrown if an error occurred while retrieving entities</exception>
/// <returns>A list of all entities</returns>
Task<List<TEntityDto>> GetAllAsync(CancellationToken cancellationToken = default);
Example:
var entities = await service.GetAllAsync();
IModernQueryService<TEntityDto, TEntityDbo, TId>
has the following methods to get count of entities:
/// <summary>
/// Returns the total count of entities
/// </summary>
/// <exception cref="InternalErrorException">Thrown if an error occurred while retrieving entities</exception>
/// <returns>Count of entities</returns>
Task<long> CountAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Returns the total count of entities that match the given <paramref name="predicate"/>
/// </summary>
/// <param name="predicate">A function to test each element for condition</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
/// <exception cref="ArgumentNullException">Thrown if provided predicate is null</exception>
/// <exception cref="InternalErrorException">Thrown if an error occurred while retrieving entities</exception>
/// <returns>Count of entities</returns>
Task<long> CountAsync(Expression<Func<TEntityDbo, bool>> predicate, CancellationToken cancellationToken = default);
CountAsync
can retrieve count of all entites or entities filtered by the given expression.
Example:
// Get count of all entities
var entities = await service.CountAsync();
// Get count of filtered entities
var entities = await service.CountAsync(x => x.Price > 500);
IModernQueryService<TEntityDto, TEntityDbo, TId>
has the following method to check entity for existence:
/// <summary>
/// Determines whether the data store contains at least one entity that matches the given <paramref name="predicate"/>
/// </summary>
/// <param name="predicate">A function to test each element for condition</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
/// <exception cref="ArgumentNullException">Thrown if provided predicate is null</exception>
/// <exception cref="InternalErrorException">Thrown if an error occurred while retrieving entities</exception>
/// <returns><see langword="true"/> if at least one entity exists; otherwise, <see langword="false"/></returns>
Task<bool> ExistsAsync(Expression<Func<TEntityDbo, bool>> predicate, CancellationToken cancellationToken = default);
Example:
var exists = await service.ExistsAsync(x => x.City == "London");
IModernQueryService<TEntityDto, TEntityDbo, TId>
has the following methods to get one entity:
/// <summary>
/// Returns the first entity that matches the given <paramref name="predicate"/>
/// </summary>
/// <param name="predicate">A function to test each element for condition</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
/// <exception cref="ArgumentNullException">Thrown if provided predicate is null</exception>
/// <exception cref="InternalErrorException">Thrown if an error occurred while retrieving entities</exception>
/// <returns>Entity that matches the given <paramref name="predicate"/> or <see langword="null"/> if entity not found</returns>
Task<TEntityDto?> FirstOrDefaultAsync(Expression<Func<TEntityDbo, bool>> predicate, CancellationToken cancellationToken = default);
/// <summary>
/// Returns the single entity that matches the given <paramref name="predicate"/>
/// </summary>
/// <param name="predicate">A function to test each element for condition</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
/// <exception cref="ArgumentNullException">Thrown if provided predicate is null</exception>
/// <exception cref="InternalErrorException">Thrown if an error occurred while retrieving entities</exception>
/// <exception cref="InvalidOperationException">Thrown if the data store contains more than one entity that matches the condition</exception>
/// <returns>Entity that matches the given <paramref name="predicate"/> or <see langword="null"/> if entity not found</returns>
Task<TEntityDto?> SingleOrDefaultAsync(Expression<Func<TEntityDbo, bool>> predicate, CancellationToken cancellationToken = default);
Both methods retrieve an entity by the given filtering expression.
The main difference between them is that FirstOrDefaultAsync
method retrieves the first entity found in the data store, while SingleOrDefaultAsync
checks if items exists twice in the data store to make sure entity is contained only once.
SingleOrDefaultAsync
performs full scan (or full index scan) of the database table (or collection) to make sure entity is contained only once. While FirstOrDefaultAsync
stops scanning the database when a first matching entity is found.
Example:
var firstEntity = await service.FirstOrDefaultAsync(x => x.City == "London");
var singleEntity = await service.SingleOrDefaultAsync(x => x.City == "London");
IModernQueryService<TEntityDto, TEntityDbo, TId>
has the following methods to get multiple entities:
/// <summary>
/// Returns all entities that match the given <paramref name="predicate"/>
/// </summary>
/// <param name="predicate">A function to test each element for condition</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
/// <exception cref="ArgumentNullException">Thrown if provided predicate is null</exception>
/// <exception cref="InternalErrorException">Thrown if an error occurred while retrieving entities</exception>
/// <returns>A list of entities that match the condition</returns>
Task<List<TEntityDto>> WhereAsync(Expression<Func<TEntityDbo, bool>> predicate, CancellationToken cancellationToken = default);
/// <summary>
/// Returns certain amount of paged entities from the data store that match the given <paramref name="predicate"/>
/// </summary>
/// <param name="predicate">A function to test each element for condition</param>
/// <param name="pageNumber">Page number. Entities to skip = (pageNumber - 1) * pageSize</param>
/// <param name="pageSize">The total number of items to select</param>
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe while waiting for the task to complete</param>
/// <exception cref="ArgumentNullException">Thrown if provided predicate is null</exception>
/// <exception cref="InternalErrorException">Thrown if an error occurred while retrieving entities</exception>
/// <returns>A list of entities that match the condition</returns>
Task<PagedResult<TEntityDto>> WhereAsync(Expression<Func<TEntityDbo, bool>> predicate, int pageNumber, int pageSize, CancellationToken cancellationToken = default);
Both methods retrieve multiple entities by the given filtering expression. The second method supports paged filtering: a user must provide a page number and page size in order to retrieve a portion of filtered entites.
Example:
var entities = await service.WhereAsync(x => x.Country == "USA");
var pagedResult = await service.WhereAsync(x => x.Country == "USA", 2, 50);
IModernQueryService<TEntityDto, TEntityDbo, TId>
has a special method that returns IQueryable:
/// <summary>
/// Returns <see cref="IQueryable{TEntity}"/> implementation
/// </summary>
/// <remarks>
/// IMPORTANT: The members of the returned <see cref="IQueryable{TEntity}"/> instance can throw implementation specific exceptions
/// </remarks>
/// <exception cref="InternalErrorException">Thrown if an error occurred while retrieving entities</exception>
/// <returns>The object typed as <see cref="IQueryable{TEntity}"/></returns>
IQueryable<TEntityDbo> AsQueryable();
This method is designed for a possibility to chain a query outside of a Repository and Service.
Example:
var entities = await service.AsQueryable(x => x.City == "London").OrderBy(x => x.Population).ToListAsync();
Refer to a specific repository implementation to see if this method is supported.