Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/dynamiclinq #146

Open
wants to merge 3 commits into
base: release
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<PackageReference Include="AutoMapper" Version="$(AutoMapper_Version)" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="$(Microsoft_EntityFrameworkCore_Version)" />
<PackageReference Include="Scrutor" Version="$(Scrutor_Version)" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.8" />
</ItemGroup>

<ItemGroup>
Expand Down
37 changes: 26 additions & 11 deletions src/Dotnet5.GraphQL3.Repositories.Abstractions/IRepository.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
Expand All @@ -23,40 +24,54 @@ public interface IRepository<TEntity, in TId>
bool Exists(TId id);
Task<bool> ExistsAsync(TId id, CancellationToken cancellationToken = default);

TEntity GetById(TId id, Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = default,
bool asTracking = default);

Task<TEntity> GetByIdAsync(TId id, CancellationToken cancellationToken,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = default,
bool asTracking = default);
TEntity GetById(TId id, Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = default, bool asTracking = default);
Task<TEntity> GetByIdAsync(TId id, CancellationToken cancellationToken, Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = default, bool asTracking = default);

void Update(TEntity entity);
Task UpdateAsync(TEntity entity, CancellationToken cancellationToken = default);

PagedResult<TEntity> GetAll(
PaginatedResult<TEntity> GetAll(
PageParams pageParams,
Expression<Func<TEntity, bool>> predicate = default,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = default,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = default,
bool asTracking = default);

PagedResult<TResult> GetAllProjections<TResult>(
PaginatedResult<TResult> GetAllDynamically<TResult>(
PageParams pageParams,
IEnumerable<string> selected,
Func<List<object>, List<TResult>> mapping,
Expression<Func<TEntity, bool>> predicate = default,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = default,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = default,
bool asTracking = default);

PaginatedResult<TResult> GetAllProjections<TResult>(
PageParams pageParams,
Expression<Func<TEntity, TResult>> selector = default,
Expression<Func<TEntity, bool>> predicate = default,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = default,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = default,
bool asTracking = default);

Task<PagedResult<TEntity>> GetAllAsync(
Task<PaginatedResult<TEntity>> GetAllAsync(
PageParams pageParams,
CancellationToken cancellationToken,
Expression<Func<TEntity, bool>> predicate = default,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = default,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = default,
bool asTracking = default);

Task<PagedResult<TResult>> GetAllProjectionsAsync<TResult>(
Task<PaginatedResult<TResult>> GetAllDynamicallyAsync<TResult>(
PageParams pageParams,
IEnumerable<string> selected,
Func<List<object>, List<TResult>> mapping,
Expression<Func<TEntity, bool>> predicate = default,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = default,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = default,
bool asTracking = default);

Task<PaginatedResult<TResult>> GetAllProjectionsAsync<TResult>(
PageParams pageParams,
CancellationToken cancellationToken,
Expression<Func<TEntity, TResult>> selector = default,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;

namespace Dotnet5.GraphQL3.Repositories.Abstractions.Pages
{
public class PaginatedResult<T>
{
private readonly int _index;
private readonly IEnumerable<T> _items;
private readonly int _size;

public PaginatedResult(IEnumerable<T> items, int index, int size)
{
_items = items;
_index = index;
_size = size;
}

public IEnumerable<T> Items
=> _items.Take(_size);

public PageInfo PageInfo
=> new()
{
Current = _index,
Size = Items.Count(),
HasNext = _size < _items.Count(),
HasPrevious = _index > 1
};

public static async Task<PaginatedResult<T>> CreateDynamicallyAsync(IQueryable source, PageParams pageParams, Func<List<object>, List<T>> mapping)
{
pageParams ??= new();
var items = await ApplyPagination(source, pageParams).ToDynamicListAsync();
return new PaginatedResult<T>(mapping(items), pageParams.Index, pageParams.Size);
}

public static PaginatedResult<T> CreateDynamically(IQueryable source, PageParams pageParams, Func<List<object>, List<T>> mapping)
{
pageParams ??= new();
var items = ApplyPagination(source, pageParams).ToDynamicList();
return new PaginatedResult<T>(mapping(items), pageParams.Index, pageParams.Size);
}

public static async Task<PaginatedResult<T>> CreateAsync(IQueryable<T> source, PageParams pageParams, CancellationToken cancellationToken)
{
pageParams ??= new();
var items = await ApplyPagination(source, pageParams).ToListAsync(cancellationToken);
return new PaginatedResult<T>(items, pageParams.Index, pageParams.Size);
}

public static PaginatedResult<T> Create(IQueryable<T> source, PageParams pageParams)
{
pageParams ??= new();
var items = ApplyPagination(source, pageParams).ToList();
return new PaginatedResult<T>(items, pageParams.Index, pageParams.Size);
}

private static IQueryable<T> ApplyPagination(IQueryable<T> source, PageParams pageParams)
=> source.Skip(pageParams.Size * (pageParams.Index - 1)).Take(pageParams.Size + 1);

private static IQueryable ApplyPagination(IQueryable source, PageParams pageParams)
=> source.Skip(pageParams.Size * (pageParams.Index - 1)).Take(pageParams.Size + 1);
}
}
64 changes: 54 additions & 10 deletions src/Dotnet5.GraphQL3.Repositories.Abstractions/Repository.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using AutoMapper;
Expand All @@ -16,6 +18,8 @@ public abstract class Repository<TEntity, TId> : IRepository<TEntity, TId>
where TEntity : Entity<TId>
where TId : struct
{
private const string SelectorTemplate = "new({0})";

private readonly IConfigurationProvider _configuration;
private readonly DbSet<TEntity> _dbSet;

Expand Down Expand Up @@ -97,7 +101,7 @@ public virtual async Task UpdateAsync(TEntity entity, CancellationToken cancella
_dbSet.Update(entity);
}

public PagedResult<TEntity> GetAll(
public PaginatedResult<TEntity> GetAll(
PageParams pageParams,
Expression<Func<TEntity, bool>> predicate = default,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = default,
Expand All @@ -110,10 +114,10 @@ public PagedResult<TEntity> GetAll(
query = predicate is null ? query : query.Where(predicate);
query = orderBy is null ? query : orderBy(query);

return PagedResult<TEntity>.Create(query, pageParams);
return PaginatedResult<TEntity>.Create(query, pageParams);
}

public PagedResult<TResult> GetAllProjections<TResult>(
public PaginatedResult<TResult> GetAllProjections<TResult>(
PageParams pageParams,
Expression<Func<TEntity, TResult>> selector = default,
Expression<Func<TEntity, bool>> predicate = default,
Expand All @@ -128,11 +132,31 @@ public PagedResult<TResult> GetAllProjections<TResult>(
query = orderBy is null ? query : orderBy(query);

return selector is null
? PagedResult<TResult>.Create(_dbSet.ProjectTo<TResult>(_configuration), pageParams)
: PagedResult<TResult>.Create(query.Select(selector), pageParams);
? PaginatedResult<TResult>.Create(_dbSet.ProjectTo<TResult>(_configuration), pageParams)
: PaginatedResult<TResult>.Create(query.Select(selector), pageParams);
}

public PaginatedResult<TResult> GetAllDynamically<TResult>(
PageParams pageParams,
IEnumerable<string> selected,
Func<List<object>, List<TResult>> mapping,
Expression<Func<TEntity, bool>> predicate = default,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = default,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = default,
bool asTracking = default)
{
var query = asTracking ? _dbSet.AsTracking() : _dbSet.AsNoTrackingWithIdentityResolution();

public Task<PagedResult<TEntity>> GetAllAsync(
query = include is null ? query : include(query);
query = predicate is null ? query : query.Where(predicate);
query = orderBy is null ? query : orderBy(query);

var selector = string.Format(SelectorTemplate, string.Join(',', selected));

return PaginatedResult<TResult>.CreateDynamically(query.Select(selector), pageParams, mapping);
}

public Task<PaginatedResult<TEntity>> GetAllAsync(
PageParams pageParams,
CancellationToken cancellationToken,
Expression<Func<TEntity, bool>> predicate = default,
Expand All @@ -146,10 +170,30 @@ public Task<PagedResult<TEntity>> GetAllAsync(
query = predicate is null ? query : query.Where(predicate);
query = orderBy is null ? query : orderBy(query);

return PagedResult<TEntity>.CreateAsync(query, pageParams, cancellationToken);
return PaginatedResult<TEntity>.CreateAsync(query, pageParams, cancellationToken);
}

public async Task<PaginatedResult<TResult>> GetAllDynamicallyAsync<TResult>(
PageParams pageParams,
IEnumerable<string> selected,
Func<List<object>, List<TResult>> mapping,
Expression<Func<TEntity, bool>> predicate = default,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = default,
Func<IQueryable<TEntity>, IIncludableQueryable<TEntity, object>> include = default,
bool asTracking = default)
{
var query = asTracking ? _dbSet.AsTracking() : _dbSet.AsNoTrackingWithIdentityResolution();

query = include is null ? query : include(query);
query = predicate is null ? query : query.Where(predicate);
query = orderBy is null ? query : orderBy(query);

var selector = string.Format(SelectorTemplate, string.Join(',', selected));

return await PaginatedResult<TResult>.CreateDynamicallyAsync(query.Select(selector), pageParams, mapping);
}

public Task<PagedResult<TResult>> GetAllProjectionsAsync<TResult>(
public Task<PaginatedResult<TResult>> GetAllProjectionsAsync<TResult>(
PageParams pageParams,
CancellationToken cancellationToken,
Expression<Func<TEntity, TResult>> selector = default,
Expand All @@ -165,8 +209,8 @@ public Task<PagedResult<TResult>> GetAllProjectionsAsync<TResult>(
query = orderBy is null ? query : orderBy(query);

return selector is null
? PagedResult<TResult>.CreateAsync(_dbSet.ProjectTo<TResult>(_configuration), pageParams, cancellationToken)
: PagedResult<TResult>.CreateAsync(query.Select(selector), pageParams, cancellationToken);
? PaginatedResult<TResult>.CreateAsync(_dbSet.ProjectTo<TResult>(_configuration), pageParams, cancellationToken)
: PaginatedResult<TResult>.CreateAsync(query.Select(selector), pageParams, cancellationToken);
}
}
}
Loading