mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-04 03:27:14 -05: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