Reworking the local repository abstract class to handle an internal class

This commit is contained in:
Zoe Roux 2020-08-16 01:02:16 +02:00
parent 8f6578dfb9
commit 76e28d6ac2
3 changed files with 111 additions and 61 deletions

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using JetBrains.Annotations;
using Kyoo.CommonApi; using Kyoo.CommonApi;
using Kyoo.Models; using Kyoo.Models;
using Kyoo.Models.Exceptions; using Kyoo.Models.Exceptions;
@ -12,81 +13,52 @@ using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public abstract class LocalRepository<T, TInternal> : IRepository<T> public abstract class LocalRepository<T>
where T : class, IResource where T : class, IResource
where TInternal : class, T
{ {
private readonly DbContext _database; protected readonly DbContext Database;
protected abstract Expression<Func<TInternal, object>> DefaultSort { get; }
protected abstract Expression<Func<T, object>> DefaultSort { get; }
protected LocalRepository(DbContext database) protected LocalRepository(DbContext database)
{ {
_database = database; Database = database;
} }
public virtual void Dispose() public virtual void Dispose()
{ {
_database.Dispose(); Database.Dispose();
} }
public virtual ValueTask DisposeAsync() public virtual ValueTask DisposeAsync()
{ {
return _database.DisposeAsync(); return Database.DisposeAsync();
}
public Task<T> Get(int id)
{
return _Get(id).Cast<T>();
} }
public Task<T> Get(string slug) public virtual Task<T> Get(int id)
{ {
return _Get(slug).Cast<T>(); return Database.Set<T>().FirstOrDefaultAsync(x => x.ID == id);
}
protected virtual Task<TInternal> _Get(int id)
{
return _database.Set<TInternal>().FirstOrDefaultAsync(x => x.ID == id);
} }
protected virtual Task<TInternal> _Get(string slug) public virtual Task<T> Get(string slug)
{ {
return _database.Set<TInternal>().FirstOrDefaultAsync(x => x.Slug == slug); return Database.Set<T>().FirstOrDefaultAsync(x => x.Slug == slug);
} }
public abstract Task<ICollection<T>> Search(string query);
public virtual Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null, public virtual Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null,
Sort<T> sort = default, Sort<T> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
return ApplyFilters(_database.Set<TInternal>(), where, sort, limit); return ApplyFilters(Database.Set<T>(), where, sort, limit);
} }
protected async Task<ICollection<T>> ApplyFilters(IQueryable<TInternal> query, protected Task<ICollection<T>> ApplyFilters(IQueryable<T> query,
Expression<Func<T, bool>> where = null, Expression<Func<T, bool>> where = null,
Sort<T> sort = default, Sort<T> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
ICollection<TInternal> items = await ApplyFilters(query, return ApplyFilters(query, Get, DefaultSort, where, sort, limit);
_Get,
DefaultSort,
where.Convert<Func<TInternal, bool>>(),
sort.To<TInternal>(),
limit);
return items.ToList<T>();
}
protected async Task<ICollection<T>> ApplyFilters(IQueryable<TInternal> query,
Expression<Func<TInternal, bool>> where = null,
Sort<TInternal> sort = default,
Pagination limit = default)
{
ICollection<TInternal> items = await ApplyFilters(query, _Get, DefaultSort, where, sort, limit);
return items.ToList<T>();
} }
protected async Task<ICollection<TValue>> ApplyFilters<TValue>(IQueryable<TValue> query, protected async Task<ICollection<TValue>> ApplyFilters<TValue>(IQueryable<TValue> query,
@ -123,7 +95,7 @@ namespace Kyoo.Controllers
return await query.ToListAsync(); return await query.ToListAsync();
} }
public abstract Task<T> Create(T obj); public abstract Task<T> Create(T obj);
public virtual async Task<T> CreateIfNotExists(T obj) public virtual async Task<T> CreateIfNotExists(T obj)
@ -146,13 +118,13 @@ namespace Kyoo.Controllers
return old; return old;
} }
} }
public virtual async Task<T> Edit(T edited, bool resetOld) public virtual async Task<T> Edit(T edited, bool resetOld)
{ {
if (edited == null) if (edited == null)
throw new ArgumentNullException(nameof(edited)); throw new ArgumentNullException(nameof(edited));
TInternal old = (TInternal)await Get(edited.Slug); T old = await Get(edited.Slug);
if (old == null) if (old == null)
throw new ItemNotFound($"No ressource found with the slug {edited.Slug}."); throw new ItemNotFound($"No ressource found with the slug {edited.Slug}.");
@ -161,25 +133,25 @@ namespace Kyoo.Controllers
Utility.Nullify(old); Utility.Nullify(old);
Utility.Merge(old, edited); Utility.Merge(old, edited);
await Validate(old); await Validate(old);
await _database.SaveChangesAsync(); await Database.SaveChangesAsync();
return old; return old;
} }
protected virtual Task Validate(TInternal ressource) protected virtual Task Validate(T ressource)
{ {
foreach (PropertyInfo property in typeof(TInternal).GetProperties() foreach (PropertyInfo property in typeof(T).GetProperties()
.Where(x => typeof(IEnumerable).IsAssignableFrom(x.PropertyType) .Where(x => typeof(IEnumerable).IsAssignableFrom(x.PropertyType)
&& !typeof(string).IsAssignableFrom(x.PropertyType))) && !typeof(string).IsAssignableFrom(x.PropertyType)))
{ {
object value = property.GetValue(ressource); object value = property.GetValue(ressource);
if (value is ICollection || value == null) if (value is ICollection || value == null)
continue; continue;
value = Utility.RunGenericMethod(typeof(Enumerable), "ToList", Utility.GetEnumerableType((IEnumerable)value), new [] { value}); value = Utility.RunGenericMethod(typeof(Enumerable), "ToList", Utility.GetEnumerableType((IEnumerable)value), value);
property.SetValue(ressource, value); property.SetValue(ressource, value);
} }
return Task.CompletedTask; return Task.CompletedTask;
} }
public virtual async Task Delete(int id) public virtual async Task Delete(int id)
{ {
T ressource = await Get(id); T ressource = await Get(id);
@ -212,4 +184,83 @@ namespace Kyoo.Controllers
await Delete(slug); await Delete(slug);
} }
} }
public abstract class LocalRepository<T, TInternal> : LocalRepository<TInternal>, IRepository<T>
where T : class, IResource
where TInternal : class, T, new()
{
protected LocalRepository(DbContext database) : base(database) { }
public new Task<T> Get(int id)
{
return base.Get(id).Cast<T>();
}
public new Task<T> Get(string slug)
{
return base.Get(slug).Cast<T>();
}
public abstract Task<ICollection<T>> Search(string query);
public virtual Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null,
Sort<T> sort = default,
Pagination limit = default)
{
return ApplyFilters(Database.Set<TInternal>(), where, sort, limit);
}
protected virtual async Task<ICollection<T>> ApplyFilters(IQueryable<TInternal> query,
Expression<Func<T, bool>> where = null,
Sort<T> sort = default,
Pagination limit = default)
{
ICollection<TInternal> items = await ApplyFilters(query,
base.Get,
DefaultSort,
where.Convert<Func<TInternal, bool>>(),
sort.To<TInternal>(),
limit);
return items.ToList<T>();
}
public abstract override Task<TInternal> Create(TInternal obj);
Task<T> IRepository<T>.Create(T item)
{
TInternal obj = new TInternal();
Utility.Assign(obj, item);
return Create(obj).Cast<T>();
}
Task<T> IRepository<T>.CreateIfNotExists(T item)
{
TInternal obj = new TInternal();
Utility.Assign(obj, item);
return CreateIfNotExists(obj).Cast<T>();
}
public Task<T> Edit(T edited, bool resetOld)
{
TInternal obj = new TInternal();
Utility.Assign(obj, edited);
return base.Edit(obj, resetOld).Cast<T>();
}
public abstract override Task Delete([NotNull] TInternal obj);
Task IRepository<T>.Delete(T obj)
{
TInternal item = new TInternal();
Utility.Assign(item, obj);
return Delete(item);
}
public virtual async Task DeleteRange(IEnumerable<T> objs)
{
foreach (T obj in objs)
await ((IRepository<T>)this).Delete(obj);
}
}
} }

View File

@ -50,7 +50,7 @@ namespace Kyoo.Controllers
.ToListAsync<Collection>(); .ToListAsync<Collection>();
} }
public override async Task<Collection> Create(Collection obj) public override async Task<CollectionDE> Create(CollectionDE obj)
{ {
if (obj == null) if (obj == null)
throw new ArgumentNullException(nameof(obj)); throw new ArgumentNullException(nameof(obj));
@ -60,11 +60,10 @@ namespace Kyoo.Controllers
return obj; return obj;
} }
public override async Task Delete(Collection item) public override async Task Delete(CollectionDE obj)
{ {
if (item == null) if (obj == null)
throw new ArgumentNullException(nameof(item)); throw new ArgumentNullException(nameof(obj));
CollectionDE obj = new CollectionDE(item);
_database.Entry(obj).State = EntityState.Deleted; _database.Entry(obj).State = EntityState.Deleted;
if (obj.Links != null) if (obj.Links != null)

View File

@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore;
namespace Kyoo.Controllers namespace Kyoo.Controllers
{ {
public class EpisodeRepository : LocalRepository<Episode, Episode>, IEpisodeRepository public class EpisodeRepository : LocalRepository<Episode>, IEpisodeRepository
{ {
private readonly DatabaseContext _database; private readonly DatabaseContext _database;
private readonly IProviderRepository _providers; private readonly IProviderRepository _providers;
@ -87,7 +87,7 @@ namespace Kyoo.Controllers
&& x.AbsoluteNumber == absoluteNumber); && x.AbsoluteNumber == absoluteNumber);
} }
public override async Task<ICollection<Episode>> Search(string query) public async Task<ICollection<Episode>> Search(string query)
{ {
return await _database.Episodes return await _database.Episodes
.Where(x => EF.Functions.ILike(x.Title, $"%{query}%")) .Where(x => EF.Functions.ILike(x.Title, $"%{query}%"))
@ -133,7 +133,7 @@ namespace Kyoo.Controllers
Sort<Episode> sort = default, Sort<Episode> sort = default,
Pagination limit = default) Pagination limit = default)
{ {
ICollection<Episode> episodes = await ApplyFilters<Episode>(_database.Episodes.Where(x => x.ShowID == showID), ICollection<Episode> episodes = await ApplyFilters(_database.Episodes.Where(x => x.ShowID == showID),
where, where,
sort, sort,
limit); limit);