mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-07 10:14:13 -04:00
Recreating the Load method using repositories and pattern matching
This commit is contained in:
parent
56c7339816
commit
14e4772dd1
@ -61,11 +61,11 @@ namespace Kyoo.Controllers
|
|||||||
Task<Studio> GetStudio(Expression<Func<Studio, bool>> where);
|
Task<Studio> GetStudio(Expression<Func<Studio, bool>> where);
|
||||||
Task<People> GetPerson(Expression<Func<People, bool>> where);
|
Task<People> GetPerson(Expression<Func<People, bool>> where);
|
||||||
|
|
||||||
Task Load<T, T2>([NotNull] T obj, Expression<Func<T, T2>> member)
|
Task Load<T, T2>([NotNull] T obj, [CanBeNull] Expression<Func<T, T2>> member)
|
||||||
where T : class, IResource
|
where T : class, IResource
|
||||||
where T2 : class;
|
where T2 : class, IResource;
|
||||||
|
|
||||||
Task Load<T, T2>([NotNull] T obj, Expression<Func<T, IEnumerable<T2>>> member)
|
Task Load<T, T2>([NotNull] T obj, [CanBeNull] Expression<Func<T, ICollection<T2>>> member)
|
||||||
where T : class, IResource
|
where T : class, IResource
|
||||||
where T2 : class;
|
where T2 : class;
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ namespace Kyoo.Controllers
|
|||||||
AfterID = afterID;
|
AfterID = afterID;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static implicit operator Pagination(int limit) => new Pagination(limit);
|
public static implicit operator Pagination(int limit) => new(limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Sort<T>
|
public struct Sort<T>
|
||||||
@ -67,7 +67,7 @@ namespace Kyoo.Controllers
|
|||||||
|
|
||||||
public Sort<TValue> To<TValue>()
|
public Sort<TValue> To<TValue>()
|
||||||
{
|
{
|
||||||
return new Sort<TValue>(Key.Convert<Func<TValue, object>>(), Descendant);
|
return new(Key.Convert<Func<TValue, object>>(), Descendant);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,6 +206,27 @@ namespace Kyoo.Controllers
|
|||||||
Pagination limit = default
|
Pagination limit = default
|
||||||
) => GetFromPeople(showSlug, where, new Sort<ShowRole>(sort), limit);
|
) => GetFromPeople(showSlug, where, new Sort<ShowRole>(sort), limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IProviderRepository : IRepository<ProviderID> {}
|
public interface IProviderRepository : IRepository<ProviderID>
|
||||||
|
{
|
||||||
|
Task<ICollection<MetadataID>> GetFromShow(int showID,
|
||||||
|
Expression<Func<MetadataID, bool>> where = null,
|
||||||
|
Sort<MetadataID> sort = default,
|
||||||
|
Pagination limit = default);
|
||||||
|
Task<ICollection<MetadataID>> GetFromShow(int showID,
|
||||||
|
[Optional] Expression<Func<MetadataID, bool>> where,
|
||||||
|
Expression<Func<MetadataID, object>> sort = default,
|
||||||
|
Pagination limit = default
|
||||||
|
) => GetFromShow(showID, where, new Sort<MetadataID>(sort), limit);
|
||||||
|
|
||||||
|
Task<ICollection<MetadataID>> GetFromShow(string showSlug,
|
||||||
|
Expression<Func<MetadataID, bool>> where = null,
|
||||||
|
Sort<MetadataID> sort = default,
|
||||||
|
Pagination limit = default);
|
||||||
|
Task<ICollection<MetadataID>> GetFromShow(string showSlug,
|
||||||
|
[Optional] Expression<Func<MetadataID, bool>> where,
|
||||||
|
Expression<Func<MetadataID, object>> sort = default,
|
||||||
|
Pagination limit = default
|
||||||
|
) => GetFromShow(showSlug, where, new Sort<MetadataID>(sort), limit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ using Kyoo.Models;
|
|||||||
|
|
||||||
namespace Kyoo.Controllers
|
namespace Kyoo.Controllers
|
||||||
{
|
{
|
||||||
public class ALibraryManager : ILibraryManager
|
public class LibraryManager : ILibraryManager
|
||||||
{
|
{
|
||||||
public ILibraryRepository LibraryRepository { get; }
|
public ILibraryRepository LibraryRepository { get; }
|
||||||
public ILibraryItemRepository LibraryItemRepository { get; }
|
public ILibraryItemRepository LibraryItemRepository { get; }
|
||||||
@ -20,7 +20,7 @@ namespace Kyoo.Controllers
|
|||||||
public IPeopleRepository PeopleRepository { get; }
|
public IPeopleRepository PeopleRepository { get; }
|
||||||
public IProviderRepository ProviderRepository { get; }
|
public IProviderRepository ProviderRepository { get; }
|
||||||
|
|
||||||
protected ALibraryManager(ILibraryRepository libraryRepository,
|
protected LibraryManager(ILibraryRepository libraryRepository,
|
||||||
ILibraryItemRepository libraryItemRepository,
|
ILibraryItemRepository libraryItemRepository,
|
||||||
ICollectionRepository collectionRepository,
|
ICollectionRepository collectionRepository,
|
||||||
IShowRepository showRepository,
|
IShowRepository showRepository,
|
||||||
@ -235,19 +235,63 @@ namespace Kyoo.Controllers
|
|||||||
return PeopleRepository.Get(where);
|
return PeopleRepository.Get(where);
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Task Load<T, T2>(T obj, Expression<Func<T, T2>> member)
|
public async Task Load<T, T2>(T obj, Expression<Func<T, T2>> member)
|
||||||
where T : class, IResource
|
where T : class, IResource
|
||||||
where T2 : class
|
where T2 : class, IResource
|
||||||
{
|
{
|
||||||
// TODO figure out why setting this method as abstract prevent the app from loading this assembly.
|
dynamic identifier = obj.ID > 0 ? obj.ID : obj.Slug;
|
||||||
throw new NotImplementedException();
|
switch (obj, (T2)default)
|
||||||
|
{
|
||||||
|
case (Show s, Studio): s.Studio = await StudioRepository.GetFromShow(identifier); break;
|
||||||
|
|
||||||
|
case (Season s, Show): s.Show = await ShowRepository.GetFromSeason(identifier); break;
|
||||||
|
|
||||||
|
case (Episode e, Show): e.Show = await ShowRepository.GetFromEpisode(identifier); break;
|
||||||
|
case (Episode e, Season): e.Season = await SeasonRepository.GetFromEpisode(identifier); break;
|
||||||
|
|
||||||
|
case (Track t, Episode): t.Episode = await EpisodeRepository.GetFromTrack(identifier); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException($"Couldn't find a way to load {member} of {typeof(T).Name}.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual Task Load<T, T2>(T obj, Expression<Func<T, IEnumerable<T2>>> member)
|
public async Task Load<T, T2>(T obj, Expression<Func<T, ICollection<T2>>> member)
|
||||||
where T : class, IResource
|
where T : class, IResource
|
||||||
where T2 : class
|
where T2 : class
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
dynamic identifier = obj.ID > 0 ? obj.ID : obj.Slug;
|
||||||
|
switch (obj, (T2)default)
|
||||||
|
{
|
||||||
|
case (Library l, ProviderID): l.Providers = await ProviderRepository.GetFromLibrary(identifier); break;
|
||||||
|
case (Library l, Show): l.Shows = await ShowRepository.GetFromLibrary(identifier); break;
|
||||||
|
case (Library l, Collection): l.Collections = await CollectionRepository.GetFromLibrary(identifier); break;
|
||||||
|
|
||||||
|
case (Collection c, Show): c.Shows = await ShowRepository.GetFromCollection(identifier); break;
|
||||||
|
case (Collection c, Library): c.Libraries = await LibraryRepository.GetFromCollection(identifier); break;
|
||||||
|
|
||||||
|
case (Show s, MetadataID): s.ExternalIDs = await ProviderRepository.GetFromShow(identifier); break;
|
||||||
|
case (Show s, Genre): s.Genres = await GenreRepository.GetFromShow(identifier); break;
|
||||||
|
case (Show s, PeopleRole): s.People = await PeopleRepository.GetFromShow(identifier); break;
|
||||||
|
case (Show s, Season): s.Seasons = await SeasonRepository.GetFromShow(identifier); break;
|
||||||
|
case (Show s, Episode): s.Episodes = await EpisodeRepository.GetFromShow(identifier); break;
|
||||||
|
case (Show s, Library): s.Libraries = await LibraryRepository.GetFromShow(identifier); break;
|
||||||
|
case (Show s, Collection): s.Collections = await CollectionRepository.GetFromShow(identifier); break;
|
||||||
|
|
||||||
|
case (Season s, MetadataID): s.ExternalIDs = await ProviderRepository.GetFromSeason(identifier); break;
|
||||||
|
case (Season s, Episode): s.Episodes = await EpisodeRepository.GetFromSeason(identifier); break;
|
||||||
|
|
||||||
|
case (Episode e, MetadataID): e.ExternalIDs = await ProviderRepository.GetFromEpisode(identifier); break;
|
||||||
|
case (Episode e, Track): e.Tracks = await TrackRepository.GetFromEpisode(identifier); break;
|
||||||
|
|
||||||
|
case (Genre g, Show): g.Shows = await ShowRepository.GetFromGenre(identifier); break;
|
||||||
|
|
||||||
|
case (Studio s, Show): s.Shows = await ShowRepository.GetFromStudio(identifier); break;
|
||||||
|
|
||||||
|
case (People p, MetadataID): p.ExternalIDs = await ProviderRepository.GetFromPeople(identifier); break;
|
||||||
|
case (People p, PeopleRole): p.Roles = await ShowRepository.GetFromPeople(identifier); break;
|
||||||
|
|
||||||
|
default: throw new ArgumentException($"Couldn't find a way to load {member} of {typeof(T).Name}.");
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<ICollection<Library>> GetLibraries(Expression<Func<Library, bool>> where = null,
|
public Task<ICollection<Library>> GetLibraries(Expression<Func<Library, bool>> where = null,
|
@ -43,26 +43,21 @@ namespace Kyoo
|
|||||||
(string inner, _, ParameterExpression p) = _innerRewrites.FirstOrDefault(x => x.param == node.Expression);
|
(string inner, _, ParameterExpression p) = _innerRewrites.FirstOrDefault(x => x.param == node.Expression);
|
||||||
if (inner != null)
|
if (inner != null)
|
||||||
{
|
{
|
||||||
Expression param = p;
|
Expression param = inner.Split('.').Aggregate<string, Expression>(p, Expression.Property);
|
||||||
foreach (string accessor in inner.Split('.'))
|
|
||||||
param = Expression.Property(param, accessor);
|
|
||||||
node = Expression.Property(param, node.Member.Name);
|
node = Expression.Property(param, node.Member.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Can't use node.Member directly because we want to support attribute override
|
// Can't use node.Member directly because we want to support attribute override
|
||||||
MemberInfo member = node.Expression.Type.GetProperty(node.Member.Name) ?? node.Member;
|
MemberInfo member = node.Expression!.Type.GetProperty(node.Member.Name) ?? node.Member;
|
||||||
ExpressionRewriteAttribute attr = member!.GetCustomAttribute<ExpressionRewriteAttribute>();
|
ExpressionRewriteAttribute attr = member!.GetCustomAttribute<ExpressionRewriteAttribute>();
|
||||||
if (attr == null)
|
if (attr == null)
|
||||||
return base.VisitMember(node);
|
return base.VisitMember(node);
|
||||||
|
|
||||||
Expression property = node.Expression;
|
Expression property = attr.Link.Split('.').Aggregate(node.Expression, Expression.Property);
|
||||||
foreach (string child in attr.Link.Split('.'))
|
|
||||||
property = Expression.Property(property, child);
|
|
||||||
|
|
||||||
if (property is MemberExpression expr)
|
if (property is MemberExpression expr)
|
||||||
Visit(expr.Expression);
|
Visit(expr.Expression);
|
||||||
_inner = attr.Inner;
|
_inner = attr.Inner;
|
||||||
return property;
|
return property!;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Expression VisitLambda<T>(Expression<T> node)
|
protected override Expression VisitLambda<T>(Expression<T> node)
|
||||||
|
@ -17,7 +17,7 @@ namespace Kyoo
|
|||||||
{
|
{
|
||||||
public static class Utility
|
public static class Utility
|
||||||
{
|
{
|
||||||
public static bool IsPropertyExpression<T, T2>(Expression<Func<T, T2>> ex)
|
public static bool IsPropertyExpression(LambdaExpression ex)
|
||||||
{
|
{
|
||||||
return ex == null ||
|
return ex == null ||
|
||||||
ex.Body is MemberExpression ||
|
ex.Body is MemberExpression ||
|
||||||
@ -490,6 +490,18 @@ namespace Kyoo
|
|||||||
return first.SequenceEqual(second, new LinkComparer<T, T1, T2>());
|
return first.SequenceEqual(second, new LinkComparer<T, T1, T2>());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static string GetMemberName([NotNull] Expression expression)
|
||||||
|
{
|
||||||
|
var t = ExpressionRewrite.Rewrite(expression);
|
||||||
|
return ExpressionRewrite.Rewrite(expression) switch
|
||||||
|
{
|
||||||
|
MemberExpression member => member.Member.Name,
|
||||||
|
LambdaExpression lambda when IsPropertyExpression(lambda) => GetMemberName(lambda.Body),
|
||||||
|
null => throw new ArgumentNullException(nameof(expression)),
|
||||||
|
_ => throw new ArgumentException($"Can't get member.")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static Expression<T> Convert<T>([CanBeNull] this Expression expr)
|
public static Expression<T> Convert<T>([CanBeNull] this Expression expr)
|
||||||
where T : Delegate
|
where T : Delegate
|
||||||
{
|
{
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
|
|
||||||
namespace Kyoo.Controllers
|
|
||||||
{
|
|
||||||
public class LibraryManager : ALibraryManager
|
|
||||||
{
|
|
||||||
private readonly DbContext _database;
|
|
||||||
|
|
||||||
public LibraryManager(ILibraryRepository libraryRepository,
|
|
||||||
ILibraryItemRepository libraryItemRepository,
|
|
||||||
ICollectionRepository collectionRepository,
|
|
||||||
IShowRepository showRepository,
|
|
||||||
ISeasonRepository seasonRepository,
|
|
||||||
IEpisodeRepository episodeRepository,
|
|
||||||
ITrackRepository trackRepository,
|
|
||||||
IGenreRepository genreRepository,
|
|
||||||
IStudioRepository studioRepository,
|
|
||||||
IProviderRepository providerRepository,
|
|
||||||
IPeopleRepository peopleRepository,
|
|
||||||
DbContext database)
|
|
||||||
: base(libraryRepository,
|
|
||||||
libraryItemRepository,
|
|
||||||
collectionRepository,
|
|
||||||
showRepository,
|
|
||||||
seasonRepository,
|
|
||||||
episodeRepository,
|
|
||||||
trackRepository,
|
|
||||||
genreRepository,
|
|
||||||
studioRepository,
|
|
||||||
providerRepository,
|
|
||||||
peopleRepository)
|
|
||||||
{
|
|
||||||
_database = database;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task Load<T, T2>(T obj, Expression<Func<T, IEnumerable<T2>>> member)
|
|
||||||
{
|
|
||||||
if (obj == null)
|
|
||||||
throw new ArgumentNullException(nameof(obj));
|
|
||||||
if (!Utility.IsPropertyExpression(member) || member == null)
|
|
||||||
throw new ArgumentException($"{nameof(member)} is not a property.");
|
|
||||||
return _database.Entry(obj).Collection(member).LoadAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Task Load<T, T2>(T obj, Expression<Func<T, T2>> member)
|
|
||||||
{
|
|
||||||
if (obj == null)
|
|
||||||
throw new ArgumentNullException(nameof(obj));
|
|
||||||
if (!Utility.IsPropertyExpression(member) || member == null)
|
|
||||||
throw new ArgumentException($"{nameof(member)} is not a property.");
|
|
||||||
return _database.Entry(obj).Reference(member).LoadAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user