Skip to content

Commit

Permalink
Added IProjectionRepository and RepositoryWithMapper. (#21)
Browse files Browse the repository at this point in the history
  • Loading branch information
fiseni authored Oct 5, 2024
1 parent 5716b04 commit da09c92
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 79 deletions.
55 changes: 2 additions & 53 deletions src/QuerySpecification.EntityFrameworkCore/RepositoryBase.cs
Original file line number Diff line number Diff line change
@@ -1,32 +1,20 @@
namespace Pozitron.QuerySpecification;

public abstract class RepositoryBase<T> : IRepositoryBase<T>, IReadRepositoryBase<T> where T : class
public abstract class RepositoryBase<T> : IRepositoryBase<T> where T : class
{
private readonly DbContext _dbContext;
private readonly SpecificationEvaluator _evaluator;
private readonly PaginationSettings _paginationSettings;

protected RepositoryBase(DbContext dbContext)
: this(dbContext, SpecificationEvaluator.Default, PaginationSettings.Default)
: this(dbContext, SpecificationEvaluator.Default)
{
}
protected RepositoryBase(DbContext dbContext, SpecificationEvaluator specificationEvaluator)
: this(dbContext, specificationEvaluator, PaginationSettings.Default)
{
}
protected RepositoryBase(DbContext dbContext, PaginationSettings paginationSettings)
: this(dbContext, SpecificationEvaluator.Default, paginationSettings)
{
}
protected RepositoryBase(DbContext dbContext, SpecificationEvaluator specificationEvaluator, PaginationSettings paginationSettings)
{
_dbContext = dbContext;
_evaluator = specificationEvaluator;
_paginationSettings = paginationSettings;
}

protected abstract IQueryable<TResult> Map<TResult>(IQueryable<T> source);

public virtual async Task<T> AddAsync(T entity, CancellationToken cancellationToken = default)
{
_dbContext.Set<T>().Add(entity);
Expand Down Expand Up @@ -137,45 +125,6 @@ public virtual IAsyncEnumerable<T> AsAsyncEnumerable(Specification<T> specificat
{
return GenerateQuery(specification).AsAsyncEnumerable();
}
public virtual async Task<TResult> ProjectToFirstAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default)
{
var query = GenerateQuery(specification).AsNoTracking();

var projectedQuery = Map<TResult>(query);

var result = await projectedQuery.FirstOrDefaultAsync(cancellationToken);

return result ?? throw new EntityNotFoundException(typeof(T).Name);
}
public virtual async Task<TResult?> ProjectToFirstOrDefaultAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default)
{
var query = GenerateQuery(specification).AsNoTracking();

var projectedQuery = Map<TResult>(query);

return await projectedQuery.FirstOrDefaultAsync(cancellationToken);
}
public virtual async Task<List<TResult>> ProjectToListAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default)
{
var query = GenerateQuery(specification).AsNoTracking();

var projectedQuery = Map<TResult>(query);

return await projectedQuery.ToListAsync(cancellationToken);
}
public virtual async Task<PagedResult<TResult>> ProjectToListAsync<TResult>(Specification<T> specification, PagingFilter filter, CancellationToken cancellationToken = default)
{
var query = GenerateQuery(specification, true).AsNoTracking();
var projectedQuery = Map<TResult>(query);

var count = await projectedQuery.CountAsync(cancellationToken);
var pagination = new Pagination(_paginationSettings, count, filter);

projectedQuery = projectedQuery.ApplyPaging(pagination);
var data = await projectedQuery.ToListAsync(cancellationToken);

return new PagedResult<TResult>(data, pagination);
}

protected virtual IQueryable<T> GenerateQuery(Specification<T> specification, bool ignorePaging = false)
{
Expand Down
67 changes: 67 additions & 0 deletions src/QuerySpecification.EntityFrameworkCore/RepositoryWithMapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
namespace Pozitron.QuerySpecification;

public abstract class RepositoryWithMapper<T> : RepositoryBase<T>, IProjectionRepository<T> where T : class
{
private readonly PaginationSettings _paginationSettings = PaginationSettings.Default;

protected RepositoryWithMapper(DbContext dbContext)
: base(dbContext)
{
}
protected RepositoryWithMapper(DbContext dbContext, SpecificationEvaluator specificationEvaluator)
: base(dbContext, specificationEvaluator)
{
}
protected RepositoryWithMapper(DbContext dbContext, PaginationSettings paginationSettings)
: base(dbContext)
{
_paginationSettings = paginationSettings;
}
protected RepositoryWithMapper(DbContext dbContext, SpecificationEvaluator specificationEvaluator, PaginationSettings paginationSettings)
: base(dbContext, specificationEvaluator)
{
_paginationSettings = paginationSettings;
}

protected abstract IQueryable<TResult> Map<TResult>(IQueryable<T> source);

public virtual async Task<TResult> ProjectToFirstAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default)
{
var query = GenerateQuery(specification).AsNoTracking();

var projectedQuery = Map<TResult>(query);

var result = await projectedQuery.FirstOrDefaultAsync(cancellationToken);

return result ?? throw new EntityNotFoundException(typeof(T).Name);
}
public virtual async Task<TResult?> ProjectToFirstOrDefaultAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default)
{
var query = GenerateQuery(specification).AsNoTracking();

var projectedQuery = Map<TResult>(query);

return await projectedQuery.FirstOrDefaultAsync(cancellationToken);
}
public virtual async Task<List<TResult>> ProjectToListAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default)
{
var query = GenerateQuery(specification).AsNoTracking();

var projectedQuery = Map<TResult>(query);

return await projectedQuery.ToListAsync(cancellationToken);
}
public virtual async Task<PagedResult<TResult>> ProjectToListAsync<TResult>(Specification<T> specification, PagingFilter filter, CancellationToken cancellationToken = default)
{
var query = GenerateQuery(specification, true).AsNoTracking();
var projectedQuery = Map<TResult>(query);

var count = await projectedQuery.CountAsync(cancellationToken);
var pagination = new Pagination(_paginationSettings, count, filter);

projectedQuery = projectedQuery.ApplyPaging(pagination);
var data = await projectedQuery.ToListAsync(cancellationToken);

return new PagedResult<TResult>(data, pagination);
}
}
9 changes: 9 additions & 0 deletions src/QuerySpecification/IProjectionRepository.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Pozitron.QuerySpecification;

public interface IProjectionRepository<T> where T : class
{
Task<TResult> ProjectToFirstAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default);
Task<TResult?> ProjectToFirstOrDefaultAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default);
Task<List<TResult>> ProjectToListAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default);
Task<PagedResult<TResult>> ProjectToListAsync<TResult>(Specification<T> specification, PagingFilter filter, CancellationToken cancellationToken = default);
}
5 changes: 0 additions & 5 deletions src/QuerySpecification/IReadRepositoryBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,5 @@ public interface IReadRepositoryBase<T> where T : class
Task<bool> AnyAsync(CancellationToken cancellationToken = default);
Task<bool> AnyAsync(Specification<T> specification, CancellationToken cancellationToken = default);
Task<bool> AnyAsync<TResult>(Specification<T, TResult> specification, CancellationToken cancellationToken = default);

IAsyncEnumerable<T> AsAsyncEnumerable(Specification<T> specification);
Task<TResult> ProjectToFirstAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default);
Task<TResult?> ProjectToFirstOrDefaultAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default);
Task<List<TResult>> ProjectToListAsync<TResult>(Specification<T> specification, CancellationToken cancellationToken = default);
Task<PagedResult<TResult>> ProjectToListAsync<TResult>(Specification<T> specification, PagingFilter filter, CancellationToken cancellationToken = default);
}
19 changes: 1 addition & 18 deletions src/QuerySpecification/IRepositoryBase.cs
Original file line number Diff line number Diff line change
@@ -1,28 +1,11 @@
namespace Pozitron.QuerySpecification;

public interface IRepositoryBase<T> where T : class
public interface IRepositoryBase<T> : IReadRepositoryBase<T> where T : class
{
Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
Task<IEnumerable<T>> AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
Task DeleteRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);

ValueTask<T?> FindAsync<TId>(TId id, CancellationToken cancellationToken = default) where TId : notnull;
Task<T> FirstAsync(Specification<T> specification, CancellationToken cancellationToken = default);
Task<TResult> FirstAsync<TResult>(Specification<T, TResult> specification, CancellationToken cancellationToken = default);
Task<T?> FirstOrDefaultAsync(Specification<T> specification, CancellationToken cancellationToken = default);
Task<TResult?> FirstOrDefaultAsync<TResult>(Specification<T, TResult> specification, CancellationToken cancellationToken = default);
Task<T?> SingleOrDefaultAsync(Specification<T> specification, CancellationToken cancellationToken = default);
Task<TResult?> SingleOrDefaultAsync<TResult>(Specification<T, TResult> specification, CancellationToken cancellationToken = default);
Task<List<T>> ListAsync(CancellationToken cancellationToken = default);
Task<List<T>> ListAsync(Specification<T> specification, CancellationToken cancellationToken = default);
Task<List<TResult>> ListAsync<TResult>(Specification<T, TResult> specification, CancellationToken cancellationToken = default);
Task<int> CountAsync(CancellationToken cancellationToken = default);
Task<int> CountAsync(Specification<T> specification, CancellationToken cancellationToken = default);
Task<int> CountAsync<TResult>(Specification<T, TResult> specification, CancellationToken cancellationToken = default);
Task<bool> AnyAsync(CancellationToken cancellationToken = default);
Task<bool> AnyAsync(Specification<T> specification, CancellationToken cancellationToken = default);
Task<bool> AnyAsync<TResult>(Specification<T, TResult> specification, CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Tests.Fixture;

public class Repository<T>(DbContext context) : RepositoryBase<T>(context) where T : class
public class Repository<T>(DbContext context) : RepositoryWithMapper<T>(context) where T : class
{
private static readonly Lazy<IMapper> _mapper = new(() =>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public void Constructor_SetsDbContextAndEvaluatorAndPaginationSettings()
Accessors<object>.PaginationSettingsOf(repo).Should().BeSameAs(paginationSettings);
}

public class Repository<T> : RepositoryBase<T> where T : class
public class Repository<T> : RepositoryWithMapper<T> where T : class
{
public Repository(DbContext context)
: base(context)
Expand Down Expand Up @@ -84,6 +84,6 @@ private class Accessors<T> where T : class
public static extern ref SpecificationEvaluator SpecificationEvaluatorOf(RepositoryBase<T> @this);

[UnsafeAccessor(UnsafeAccessorKind.Field, Name = "_paginationSettings")]
public static extern ref PaginationSettings PaginationSettingsOf(RepositoryBase<T> @this);
public static extern ref PaginationSettings PaginationSettingsOf(RepositoryWithMapper<T> @this);
}
}

0 comments on commit da09c92

Please sign in to comment.