Compare commits

...

11 Commits

Author SHA1 Message Date
Developer 02
cc2177e6d1 Update package for net9.0 and version 2.1.0
- Added net9.0 to target frameworks.
- Updated package description to reference DigitalData.Core.Application.
- Incremented version to 2.1.0 for package, assembly, and file versions.
- Changed package icon path to `..\Assets\core_icon.png`.
2025-05-20 10:56:49 +02:00
Developer 02
94ff231a71 Update project file and increment version to 3.3.0
- Added target frameworks: net7.0, net8.0, net9.0
- Shortened package description
- Updated versioning from 3.2.1 to 3.3.0
- Changed package icon path to ../Assets/core_icon.png
2025-05-20 10:50:28 +02:00
Developer 02
5e2ebd07db Update NuGet package description for clarity
Modified the description in the `DigitalData.Core.Abstractions.csproj` file to provide a more concise statement about the package, removing details about Clean Architecture principles.
2025-05-20 10:44:56 +02:00
Developer 02
43e714b061 Update package references by target framework version
Modified `DigitalData.Core.Abstractions.csproj` to conditionally include package references for `Microsoft.Extensions.Caching.Memory` and `Microsoft.Extensions.Configuration.Binder` based on the target framework (`net7.0`, `net8.0`, `net9.0`). This change enhances compatibility and version management for different .NET versions.
2025-05-20 10:44:28 +02:00
Developer 02
7a7f564f82 Bump version to 4.0.0 in project file
Updated versioning information in `DigitalData.Core.Abstractions.csproj` from 3.6.0 to 4.0.0, including changes to `<Version>`, `<AssemblyVersion>`, and `<FileVersion>`.
2025-05-20 10:36:20 +02:00
Developer 02
f7e2bb2434 Refactor repository pattern and mark methods obsolete
Updated `IRepository<TEntity>` to introduce new reading methods and mark existing ones as obsolete. The `CRUDRepository` class is now deprecated, encouraging a transition to alternative implementations. Significant changes in `DbRepository<TDbContext, TEntity>` include the removal of old read methods in favor of a new `Read` method returning `IReadQuery<TEntity>`. Added a `ReadQuery<TEntity>` class to enhance querying capabilities with a fluent API. Obsolete methods are organized under a `#region Obsolete` directive for better management.
2025-05-20 10:35:27 +02:00
Developer 02
5995b334eb Mark ICRUDRepository as obsolete
The ICRUDRepository interface has been marked as obsolete,
with an attribute advising users to use IRepository instead.
This change aims to direct developers towards a preferred
alternative for repository operations.
2025-05-20 10:20:42 +02:00
Developer 02
1b8dc29595 Enhance IRepository<TEntity> with new method overloads
- Added overload for CreateAsync to accept IEnumerable<TEntity>.
- Updated Read method to accept multiple expressions for flexible querying.
2025-05-20 10:11:39 +02:00
Developer 02
36c2c20eab Add CancellationToken support to IReadQuery methods
Updated the IReadQuery<TEntity> interface to include an optional CancellationToken parameter for the following asynchronous methods:
- FirstOrDefaultAsync
- SingleOrDefaultAsync
- ToListAsync
- FirstAsync
- SingleAsync

Documentation comments for these methods have also been updated to describe the new cancellation parameter and its purpose.
2025-05-20 10:10:16 +02:00
Developer 02
a7df79629d Refactor cancellation token parameter names
Updated parameter names from `ct` to `cancellation` in several asynchronous methods of the `IRepository<TEntity>` interface. This change improves code readability and clarity. Affected methods include `CreateAsync`, `UpdateAsync`, `DeleteAsync`, `ReadAllAsync`, `ReadOrDefaultAsync`, and their generic counterparts.
2025-05-20 10:07:42 +02:00
Developer 02
89a77019dc Enhance IRepository and add RepositoryExtensions
Updated the IRepository<TEntity> interface to include an overloaded CreateAsync method for handling multiple entities and removed the CancellationToken from the Read method. Introduced a new RepositoryExtensions class with a Where extension method to facilitate applying multiple filters to IReadQuery<TEntity> queries.
2025-05-20 10:05:57 +02:00
10 changed files with 143 additions and 57 deletions

View File

@ -9,7 +9,7 @@
<Authors>Digital Data GmbH</Authors> <Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company> <Company>Digital Data GmbH</Company>
<Product>DigitalData.Core.Abstractions</Product> <Product>DigitalData.Core.Abstractions</Product>
<Description>This package contains abstractions for the DigitalData.Core library, developed according to the principles of Clean Architecture. It promotes separation of concerns and enables independent core logic.</Description> <Description>This package contains abstractions for the DigitalData.Core library.</Description>
<PackageTags>digital data core abstractions clean architecture</PackageTags> <PackageTags>digital data core abstractions clean architecture</PackageTags>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Copyright>Copyright 2024</Copyright> <Copyright>Copyright 2024</Copyright>
@ -17,9 +17,9 @@
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl> <RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<PackAsTool>False</PackAsTool> <PackAsTool>False</PackAsTool>
<PackageIcon>core_icon.png</PackageIcon> <PackageIcon>core_icon.png</PackageIcon>
<Version>3.6.0</Version> <Version>4.0.0</Version>
<AssemblyVersion>3.6.0</AssemblyVersion> <AssemblyVersion>4.0.0</AssemblyVersion>
<FileVersion>3.6.0</FileVersion> <FileVersion>4.0.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -29,12 +29,19 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" /> <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
<PackageReference Include="System.DirectoryServices" Version="7.0.1" /> </ItemGroup>
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="7.0.1" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.5.1" /> <ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net9.0'">
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="9.0.5" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="9.0.5" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<Description>This package provides implementations for application services within the DigitalData.Core.Abstractions library. It includes generic CRUD operations using Entity Framework Core, AutoMapper integration for object mapping, and additional services such as JWT handling and directory search functionality, adhering to Clean Architecture principles to ensure separation of concerns and maintainability.</Description> <Description>This package includes generic CRUD operations using Entity Framework Core, AutoMapper integration for object mapping, and additional services such as JWT handling and directory search functionality, adhering to Clean Architecture principles.</Description>
<PackageId>DigitalData.Core.Application</PackageId> <PackageId>DigitalData.Core.Application</PackageId>
<Authors>Digital Data GmbH</Authors> <Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company> <Company>Digital Data GmbH</Company>
@ -14,13 +14,13 @@
<PackageIcon>core_icon.png</PackageIcon> <PackageIcon>core_icon.png</PackageIcon>
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl> <RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<PackageTags>digital data core application clean architecture</PackageTags> <PackageTags>digital data core application clean architecture</PackageTags>
<Version>3.2.1</Version> <Version>3.3.0</Version>
<AssemblyVersion>3.2.1</AssemblyVersion> <AssemblyVersion>3.3.0</AssemblyVersion>
<FileVersion>3.2.1</FileVersion> <FileVersion>3.3.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\nuget-package-icons\core_icon.png"> <None Include="..\Assets\core_icon.png">
<Pack>True</Pack> <Pack>True</Pack>
<PackagePath>\</PackagePath> <PackagePath>\</PackagePath>
</None> </None>

View File

@ -5,6 +5,7 @@
/// </summary> /// </summary>
/// <typeparam name="TEntity">The type of the entity this repository works with.</typeparam> /// <typeparam name="TEntity">The type of the entity this repository works with.</typeparam>
/// <typeparam name="TId">The type of the identifier for the entity.</typeparam> /// <typeparam name="TId">The type of the identifier for the entity.</typeparam>
[Obsolete("Use IRepository")]
public interface ICRUDRepository<TEntity, TId> where TEntity : class public interface ICRUDRepository<TEntity, TId> where TEntity : class
{ {
/// <summary> /// <summary>

View File

@ -21,32 +21,37 @@ public interface IReadQuery<TEntity>
/// <summary> /// <summary>
/// Asynchronously retrieves the first entity or a default value if no entity is found. /// Asynchronously retrieves the first entity or a default value if no entity is found.
/// </summary> /// </summary>
/// <param name="cancellation">A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the entity or a default value.</returns> /// <returns>A task that represents the asynchronous operation. The task result contains the entity or a default value.</returns>
public Task<TEntity?> FirstOrDefaultAsync(); public Task<TEntity?> FirstOrDefaultAsync(CancellationToken cancellation = default);
/// <summary> /// <summary>
/// Asynchronously retrieves a single entity or a default value if no entity is found. /// Asynchronously retrieves a single entity or a default value if no entity is found.
/// </summary> /// </summary>
/// <param name="cancellation">A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the entity or a default value.</returns> /// <returns>A task that represents the asynchronous operation. The task result contains the entity or a default value.</returns>
public Task<TEntity?> SingleOrDefaultAsync(); public Task<TEntity?> SingleOrDefaultAsync(CancellationToken cancellation = default);
/// <summary> /// <summary>
/// Asynchronously retrieves a list of entities. /// Asynchronously retrieves a list of entities.
/// </summary> /// </summary>
/// <param name="cancellation">A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the list of entities.</returns> /// <returns>A task that represents the asynchronous operation. The task result contains the list of entities.</returns>
public Task<IEnumerable<TEntity>> ToListAsync(); public Task<IEnumerable<TEntity>> ToListAsync(CancellationToken cancellation = default);
/// <summary> /// <summary>
/// Asynchronously retrieves the first entity. Throws an exception if no entity is found. /// Asynchronously retrieves the first entity. Throws an exception if no entity is found.
/// </summary> /// </summary>
/// <param name="cancellation">A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the first entity.</returns> /// <returns>A task that represents the asynchronous operation. The task result contains the first entity.</returns>
public Task<TEntity> FirstAsync(); public Task<TEntity> FirstAsync(CancellationToken cancellation = default);
/// <summary> /// <summary>
/// Asynchronously retrieves a single entity. Throws an exception if no entity is found. /// Asynchronously retrieves a single entity. Throws an exception if no entity is found.
/// </summary> /// </summary>
/// <param name="cancellation">A <see cref="CancellationToken"/> to observe while waiting for the task to complete.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains the single entity.</returns> /// <returns>A task that represents the asynchronous operation. The task result contains the single entity.</returns>
public Task<TEntity> SingleAsync(); public Task<TEntity> SingleAsync(CancellationToken cancellation = default);
/// <summary> /// <summary>
/// Synchronously retrieves the first entity or a default value if no entity is found. /// Synchronously retrieves the first entity or a default value if no entity is found.

View File

@ -6,25 +6,27 @@ public interface IRepository<TEntity>
{ {
public IEntityMapper<TEntity> Mapper { get; } public IEntityMapper<TEntity> Mapper { get; }
public Task<TEntity> CreateAsync(TEntity entity, CancellationToken ct = default); public Task<TEntity> CreateAsync(TEntity entity, CancellationToken cancellation = default);
public Task<IEnumerable<TEntity>> CreateAsync(IEnumerable<TEntity> entities, CancellationToken ct = default); public Task<IEnumerable<TEntity>> CreateAsync(IEnumerable<TEntity> entities, CancellationToken cancellation = default);
public IReadQuery<TEntity> Read(Expression<Func<TEntity, bool>> expression, CancellationToken ct = default); public IReadQuery<TEntity> Read(params Expression<Func<TEntity, bool>>[] expressions);
public Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken ct = default); public Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken cancellation = default);
public Task DeleteAsync(Expression<Func<TEntity, bool>> expression, CancellationToken ct = default); public Task DeleteAsync(Expression<Func<TEntity, bool>> expression, CancellationToken cancellation = default);
#region Obsolete
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public Task<IEnumerable<TEntity>> ReadAllAsync(Expression<Func<TEntity, bool>>? expression = null, CancellationToken cancellation = default);
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")] [Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public Task<IEnumerable<TEntity>> ReadAllAsync(Expression<Func<TEntity, bool>>? expression = null, CancellationToken ct = default); public Task<TEntity?> ReadOrDefaultAsync(Expression<Func<TEntity, bool>> expression, bool single = true, CancellationToken cancellation = default);
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")] [Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public Task<TEntity?> ReadOrDefaultAsync(Expression<Func<TEntity, bool>> expression, bool single = true, CancellationToken ct = default); public Task<IEnumerable<TDto>> ReadAllAsync<TDto>(Expression<Func<TEntity, bool>>? expression = null, CancellationToken cancellation = default);
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")] [Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public Task<IEnumerable<TDto>> ReadAllAsync<TDto>(Expression<Func<TEntity, bool>>? expression = null, CancellationToken ct = default); public Task<TDto?> ReadOrDefaultAsync<TDto>(Expression<Func<TEntity, bool>> expression, bool single = true, CancellationToken cancellation = default);
#endregion
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public Task<TDto?> ReadOrDefaultAsync<TDto>(Expression<Func<TEntity, bool>> expression, bool single = true, CancellationToken ct = default);
} }

View File

@ -0,0 +1,14 @@
using System.Linq.Expressions;
namespace DigitalData.Core.Application.Interfaces.Repository;
public static class RepositoryExtensions
{
public static IReadQuery<TEntity> Where<TEntity>(this IReadQuery<TEntity> query, params Expression<Func<TEntity, bool>>[] expressions)
{
foreach (var expression in expressions)
query = query.Where(expression);
return query;
}
}

View File

@ -15,6 +15,7 @@ namespace DigitalData.Core.Infrastructure
/// This repository abstracts the common database operations, offering an asynchronous API to work with the entity's data. /// This repository abstracts the common database operations, offering an asynchronous API to work with the entity's data.
/// It leverages the EF Core's DbContext and DbSet to perform these operations. /// It leverages the EF Core's DbContext and DbSet to perform these operations.
/// </remarks> /// </remarks>
[Obsolete("Use Repository")]
public class CRUDRepository<TEntity, TId, TDbContext> : ICRUDRepository<TEntity, TId> public class CRUDRepository<TEntity, TId, TDbContext> : ICRUDRepository<TEntity, TId>
where TEntity : class where TEntity : class
where TDbContext : DbContext where TDbContext : DbContext

View File

@ -33,24 +33,7 @@ public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbC
return entities; return entities;
} }
public virtual async Task<IEnumerable<TEntity>> ReadAllAsync(Expression<Func<TEntity, bool>>? expression = null, CancellationToken ct = default) public IReadQuery<TEntity> Read(params Expression<Func<TEntity, bool>>[] expressions) => new ReadQuery<TEntity>(Entities.AsNoTracking()).Where(expressions);
=> expression is null
? await Entities.AsNoTracking().ToListAsync(ct)
: await Entities.AsNoTracking().Where(expression).ToListAsync(ct);
public virtual async Task<TEntity?> ReadOrDefaultAsync(Expression<Func<TEntity, bool>> expression, bool single = true, CancellationToken ct = default)
=> single
? await Entities.AsNoTracking().Where(expression).SingleOrDefaultAsync(ct)
: await Entities.AsNoTracking().Where(expression).FirstOrDefaultAsync(ct);
public virtual async Task<IEnumerable<TDto>> ReadAllAsync<TDto>(Expression<Func<TEntity, bool>>? expression = null, CancellationToken ct = default)
=> Mapper.Map<TDto>(await ReadAllAsync(expression, ct));
public virtual async Task<TDto?> ReadOrDefaultAsync<TDto>(Expression<Func<TEntity, bool>> expression, bool single = true, CancellationToken ct = default)
{
var entity = await ReadOrDefaultAsync(expression, single, ct);
return entity is null ? default : Mapper.Map<TDto>(entity);
}
public virtual async Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken ct = default) public virtual async Task UpdateAsync<TDto>(TDto dto, Expression<Func<TEntity, bool>> expression, CancellationToken ct = default)
{ {
@ -76,4 +59,29 @@ public class DbRepository<TDbContext, TEntity> : IRepository<TEntity> where TDbC
await Context.SaveChangesAsync(ct); await Context.SaveChangesAsync(ct);
} }
#region Obsolete
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public virtual async Task<IEnumerable<TEntity>> ReadAllAsync(Expression<Func<TEntity, bool>>? expression = null, CancellationToken ct = default)
=> expression is null
? await Entities.AsNoTracking().ToListAsync(ct)
: await Entities.AsNoTracking().Where(expression).ToListAsync(ct);
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public virtual async Task<TEntity?> ReadOrDefaultAsync(Expression<Func<TEntity, bool>> expression, bool single = true, CancellationToken ct = default)
=> single
? await Entities.AsNoTracking().Where(expression).SingleOrDefaultAsync(ct)
: await Entities.AsNoTracking().Where(expression).FirstOrDefaultAsync(ct);
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public virtual async Task<IEnumerable<TDto>> ReadAllAsync<TDto>(Expression<Func<TEntity, bool>>? expression = null, CancellationToken ct = default)
=> Mapper.Map<TDto>(await ReadAllAsync(expression, ct));
[Obsolete("Use Read-method returning IReadQuery<TEntity> instead.")]
public virtual async Task<TDto?> ReadOrDefaultAsync<TDto>(Expression<Func<TEntity, bool>> expression, bool single = true, CancellationToken ct = default)
{
var entity = await ReadOrDefaultAsync(expression, single, ct);
return entity is null ? default : Mapper.Map<TDto>(entity);
}
#endregion
} }

View File

@ -6,22 +6,22 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild> <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageId>DigitalData.Core.Infrastructure</PackageId> <PackageId>DigitalData.Core.Infrastructure</PackageId>
<Version>2.0.4</Version>
<Authors>Digital Data GmbH</Authors> <Authors>Digital Data GmbH</Authors>
<Company>Digital Data GmbH</Company> <Company>Digital Data GmbH</Company>
<Product>DigitalData.Core.Infrastructure</Product> <Product>DigitalData.Core.Infrastructure</Product>
<Description>This package provides implementations for data access and other low-level services within the DigitalData.Core.Abstractions library. It includes generic CRUD operations using Entity Framework Core, database context management, and other infrastructure-related functionalities, adhering to Clean Architecture principles to ensure separation of concerns and maintainability.</Description> <Description>This package provides implementations for data access and other low-level services within the DigitalData.Core.Application library. It includes generic CRUD operations using Entity Framework Core, database context management, and other infrastructure-related functionalities, adhering to Clean Architecture principles.</Description>
<Copyright>Copyright 2024</Copyright> <Copyright>Copyright 2024</Copyright>
<PackageIcon>core_icon.png</PackageIcon> <PackageIcon>core_icon.png</PackageIcon>
<RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl> <RepositoryUrl>http://git.dd:3000/AppStd/WebCoreModules.git</RepositoryUrl>
<RepositoryType>digital data core abstractions clean architecture</RepositoryType> <RepositoryType>digital data core abstractions clean architecture</RepositoryType>
<PackageTags>digital data core infrastructure clean architecture</PackageTags> <PackageTags>digital data core infrastructure clean architecture</PackageTags>
<AssemblyVersion>2.0.4</AssemblyVersion> <Version>2.1.0</Version>
<FileVersion>2.0.4</FileVersion> <AssemblyVersion>2.1.0</AssemblyVersion>
<FileVersion>2.1.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<None Include="..\..\nuget-package-icons\core_icon.png"> <None Include="..\Assets\core_icon.png">
<Pack>True</Pack> <Pack>True</Pack>
<PackagePath>\</PackagePath> <PackagePath>\</PackagePath>
</None> </None>

View File

@ -0,0 +1,48 @@
using DigitalData.Core.Application.Interfaces.Repository;
using Microsoft.EntityFrameworkCore;
using System.Linq.Expressions;
namespace DigitalData.Core.Infrastructure;
public sealed record ReadQuery<TEntity> : IReadQuery<TEntity>
{
private IQueryable<TEntity> _query;
internal ReadQuery(IQueryable<TEntity> queryable)
{
_query = queryable;
}
public TEntity First() => _query.First();
public Task<TEntity> FirstAsync(CancellationToken cancellation = default) => _query.FirstAsync(cancellation);
public TEntity? FirstOrDefault() => _query.FirstOrDefault();
public Task<TEntity?> FirstOrDefaultAsync(CancellationToken cancellation = default) => _query.FirstOrDefaultAsync(cancellation);
public TEntity Single() => _query.Single();
public Task<TEntity> SingleAsync(CancellationToken cancellation = default) => _query.SingleAsync(cancellation);
public TEntity? SingleOrDefault() => _query.SingleOrDefault();
public Task<TEntity?> SingleOrDefaultAsync(CancellationToken cancellation = default) => _query.SingleOrDefaultAsync(cancellation);
public IEnumerable<TEntity> ToList() => _query.ToList();
public async Task<IEnumerable<TEntity>> ToListAsync(CancellationToken cancellation = default) => await _query.ToListAsync(cancellation);
public IReadQuery<TEntity> Where(Expression<Func<TEntity, bool>> expression)
{
_query = _query.Where(expression);
return this;
}
}