Fixing IEnumerables to the dotnet ef store

This commit is contained in:
Zoe Roux 2020-08-07 05:03:51 +02:00
parent 65bfe2c4cd
commit eeb0fe25e3
27 changed files with 119 additions and 103 deletions

View File

@ -121,25 +121,25 @@ namespace Kyoo.Controllers
Pagination limit = default
) => GetEpisodesFromSeason(showSlug, seasonNumber, where, new Sort<Episode>(sort), limit);
Task<ICollection<PeopleLink>> GetPeopleFromShow(int showID,
Expression<Func<PeopleLink, bool>> where = null,
Sort<PeopleLink> sort = default,
Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default);
Task<ICollection<PeopleLink>> GetPeopleFromShow(int showID,
[Optional] Expression<Func<PeopleLink, bool>> where,
Expression<Func<PeopleLink, object>> sort,
Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
[Optional] Expression<Func<PeopleRole, bool>> where,
Expression<Func<PeopleRole, object>> sort,
Pagination limit = default
) => GetPeopleFromShow(showID, where, new Sort<PeopleLink>(sort), limit);
) => GetPeopleFromShow(showID, where, new Sort<PeopleRole>(sort), limit);
Task<ICollection<PeopleLink>> GetPeopleFromShow(string showSlug,
Expression<Func<PeopleLink, bool>> where = null,
Sort<PeopleLink> sort = default,
Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default);
Task<ICollection<PeopleLink>> GetPeopleFromShow(string showSlug,
[Optional] Expression<Func<PeopleLink, bool>> where,
Expression<Func<PeopleLink, object>> sort,
Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
[Optional] Expression<Func<PeopleRole, bool>> where,
Expression<Func<PeopleRole, object>> sort,
Pagination limit = default
) => GetPeopleFromShow(showSlug, where, new Sort<PeopleLink>(sort), limit);
) => GetPeopleFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
Task<ICollection<Genre>> GetGenresFromShow(int showID,
Expression<Func<Genre, bool>> where = null,

View File

@ -12,7 +12,7 @@ namespace Kyoo.Controllers
Task<Show> GetShowByID(Show show);
Task<IEnumerable<Show>> SearchShows(string showName, bool isMovie);
Task<IEnumerable<PeopleLink>> GetPeople(Show show);
Task<IEnumerable<PeopleRole>> GetPeople(Show show);
Task<Season> GetSeason(Show show, int seasonNumber);

View File

@ -12,6 +12,6 @@ namespace Kyoo.Controllers
Task<IEnumerable<Show>> SearchShows(string showName, bool isMovie, Library library);
Task<Season> GetSeason(Show show, int seasonNumber, Library library);
Task<Episode> GetEpisode(Show show, string episodePath, int seasonNumber, int episodeNumber, int absoluteNumber, Library library);
Task<IEnumerable<PeopleLink>> GetPeople(Show show, Library library);
Task<IEnumerable<PeopleRole>> GetPeople(Show show, Library library);
}
}

View File

@ -421,25 +421,25 @@ namespace Kyoo.Controllers
public interface IPeopleRepository : IRepository<People>
{
Task<ICollection<PeopleLink>> GetFromShow(int showID,
Expression<Func<PeopleLink, bool>> where = null,
Sort<PeopleLink> sort = default,
Task<ICollection<PeopleRole>> GetFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default);
Task<ICollection<PeopleLink>> GetFromShow(int showID,
[Optional] Expression<Func<PeopleLink, bool>> where,
Expression<Func<PeopleLink, object>> sort,
Task<ICollection<PeopleRole>> GetFromShow(int showID,
[Optional] Expression<Func<PeopleRole, bool>> where,
Expression<Func<PeopleRole, object>> sort,
Pagination limit = default
) => GetFromShow(showID, where, new Sort<PeopleLink>(sort), limit);
) => GetFromShow(showID, where, new Sort<PeopleRole>(sort), limit);
Task<ICollection<PeopleLink>> GetFromShow(string showSlug,
Expression<Func<PeopleLink, bool>> where = null,
Sort<PeopleLink> sort = default,
Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default);
Task<ICollection<PeopleLink>> GetFromShow(string showSlug,
[Optional] Expression<Func<PeopleLink, bool>> where,
Expression<Func<PeopleLink, object>> sort,
Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
[Optional] Expression<Func<PeopleRole, bool>> where,
Expression<Func<PeopleRole, object>> sort,
Pagination limit = default
) => GetFromShow(showSlug, where, new Sort<PeopleLink>(sort), limit);
) => GetFromShow(showSlug, where, new Sort<PeopleRole>(sort), limit);
Task<ICollection<ShowRole>> GetFromPeople(int showID,
Expression<Func<ShowRole, bool>> where = null,

View File

@ -9,6 +9,6 @@ namespace Kyoo.Controllers
Task<Show> Validate(Show show, bool alwaysDownload = false);
Task<Season> Validate(Season season, bool alwaysDownload = false);
Task<Episode> Validate(Episode episode, bool alwaysDownload = false);
Task<IEnumerable<PeopleLink>> Validate(IEnumerable<PeopleLink> actors, bool alwaysDownload = false);
Task<IEnumerable<PeopleRole>> Validate(IEnumerable<PeopleRole> actors, bool alwaysDownload = false);
}
}

View File

@ -303,17 +303,17 @@ namespace Kyoo.Controllers
return EpisodeRepository.GetFromSeason(showSlug, seasonNumber, where, sort, limit);
}
public Task<ICollection<PeopleLink>> GetPeopleFromShow(int showID,
Expression<Func<PeopleLink, bool>> where = null,
Sort<PeopleLink> sort = default,
public Task<ICollection<PeopleRole>> GetPeopleFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default)
{
return PeopleRepository.GetFromShow(showID, where, sort, limit);
}
public Task<ICollection<PeopleLink>> GetPeopleFromShow(string showSlug,
Expression<Func<PeopleLink, bool>> where = null,
Sort<PeopleLink> sort = default,
public Task<ICollection<PeopleRole>> GetPeopleFromShow(string showSlug,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default)
{
return PeopleRepository.GetFromShow(showSlug, where, sort, limit);

View File

@ -11,7 +11,7 @@
<Company>SDG</Company>
<PackageLicenseExpression>GPL-3.0-or-later</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageVersion>1.0.21</PackageVersion>
<PackageVersion>1.0.22</PackageVersion>
</PropertyGroup>
<ItemGroup>

View File

@ -5,7 +5,7 @@ using Newtonsoft.Json;
namespace Kyoo.Models
{
public class PeopleLink : IResource
public class PeopleRole : IResource
{
[JsonIgnore] public int ID { get; set; }
[JsonIgnore] public int PeopleID { get; set; }
@ -40,9 +40,9 @@ namespace Kyoo.Models
public string Role { get; set; }
public string Type { get; set; }
public PeopleLink() {}
public PeopleRole() {}
public PeopleLink(People people, Show show, string role, string type)
public PeopleRole(People people, Show show, string role, string type)
{
People = people;
Show = show;
@ -50,7 +50,7 @@ namespace Kyoo.Models
Type = type;
}
public PeopleLink(string slug,
public PeopleRole(string slug,
string name,
string role,
string type,
@ -85,7 +85,7 @@ namespace Kyoo.Models
public ShowRole() {}
public ShowRole(PeopleLink x)
public ShowRole(PeopleRole x)
{
ID = x.ID;
Role = x.Role;
@ -105,7 +105,7 @@ namespace Kyoo.Models
IsMovie = x.Show.IsMovie;
}
public static Expression<Func<PeopleLink, ShowRole>> FromPeopleRole => x => new ShowRole
public static Expression<Func<PeopleRole, ShowRole>> FromPeopleRole => x => new ShowRole
{
ID = x.ID,
Role = x.Role,

View File

@ -12,7 +12,7 @@ namespace Kyoo.Models
public string Poster { get; set; }
public virtual IEnumerable<MetadataID> ExternalIDs { get; set; }
[JsonIgnore] public virtual IEnumerable<PeopleLink> Roles { get; set; }
[JsonIgnore] public virtual IEnumerable<PeopleRole> Roles { get; set; }
public People() {}

View File

@ -37,7 +37,7 @@ namespace Kyoo.Models
[NotMergable] [JsonIgnore] public virtual IEnumerable<GenreLink> GenreLinks { get; set; }
[JsonIgnore] public int? StudioID { get; set; }
public virtual Studio Studio { get; set; }
[JsonIgnore] public virtual IEnumerable<PeopleLink> People { get; set; }
[JsonIgnore] public virtual IEnumerable<PeopleRole> People { get; set; }
[JsonIgnore] public virtual IEnumerable<Season> Seasons { get; set; }
[JsonIgnore] public virtual IEnumerable<Episode> Episodes { get; set; }
@ -53,7 +53,7 @@ namespace Kyoo.Models
[NotMergable] [JsonIgnore] public IEnumerable<Collection> Collections
{
get => CollectionLinks.Select(x => x.Collection);
get => CollectionLinks?.Select(x => x.Collection);
set => CollectionLinks = value?.Select(x => new CollectionLink(x, this));
}
@ -126,7 +126,7 @@ namespace Kyoo.Models
foreach (GenreLink genre in GenreLinks)
genre.Show = this;
if (People != null)
foreach (PeopleLink link in People)
foreach (PeopleRole link in People)
link.Show = this;
if (Seasons != null)
foreach (Season season in Seasons)

View File

@ -79,7 +79,7 @@ namespace Kyoo.Controllers
{
{typeof(Show), new HashSet<string> {"genres", "studio"}},
{typeof(Episode), new HashSet<string> {"tracks"}},
{typeof(PeopleLink), new HashSet<string> {"show"}}
{typeof(PeopleRole), new HashSet<string> {"show"}}
})
},
context.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>(),

View File

@ -1,7 +1,9 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
using Kyoo.CommonApi;
using Kyoo.Models;
@ -136,7 +138,20 @@ namespace Kyoo.Controllers
return old;
}
protected abstract Task Validate(T ressource);
protected virtual Task Validate(T ressource)
{
foreach (PropertyInfo property in typeof(T).GetProperties()
.Where(x => typeof(IEnumerable).IsAssignableFrom(x.PropertyType)
&& !typeof(string).IsAssignableFrom(x.PropertyType)))
{
object value = property.GetValue(ressource);
if (value is ICollection || value == null)
continue;
value = Utility.RunGenericMethod(typeof(Enumerable), "ToList", Utility.GetEnumerableType((IEnumerable)value), new [] { value});
property.SetValue(ressource, value);
}
return Task.CompletedTask;
}
public virtual async Task Delete(int id)
{

View File

@ -66,9 +66,12 @@ namespace Kyoo.Controllers
public void ReloadPlugins()
{
string pluginFolder = _config.GetValue<string>("plugins");
if (!Directory.Exists(pluginFolder))
if (!Directory.Exists(pluginFolder))
{
Console.WriteLine("\nPlugin directory does not exist. No plugin loaded.\n");
return;
}
string[] pluginsPaths = Directory.GetFiles(pluginFolder);
_plugins = pluginsPaths.SelectMany(path =>
{
@ -83,20 +86,21 @@ namespace Kyoo.Controllers
}
catch (Exception ex)
{
Console.Error.WriteLine($"Error loading the plugin at {path}.\nException: {ex.Message}\n\n");
return null;
Console.Error.WriteLine($"\nError loading the plugin at {path}.\n{ex.GetType().Name}: {ex.Message}\n");
return Array.Empty<IPlugin>();
}
}).Where(x => x != null).ToList();
}).ToList();
if (!_plugins.Any())
{
Console.WriteLine("No plugin enabled.");
Console.WriteLine("\nNo plugin enabled.\n");
return;
}
Console.WriteLine("Plugin enabled:");
Console.WriteLine("\nPlugin enabled:");
foreach (IPlugin plugin in _plugins)
Console.WriteLine($"\t{plugin.Name}");
Console.WriteLine();
}
}
}

View File

@ -146,9 +146,9 @@ namespace Kyoo.Controllers
return episode;
}
public async Task<IEnumerable<PeopleLink>> GetPeople(Show show, Library library)
public async Task<IEnumerable<PeopleRole>> GetPeople(Show show, Library library)
{
List<PeopleLink> people = await GetMetadata(
List<PeopleRole> people = await GetMetadata(
provider => provider.GetPeople(show),
library,
$"a cast member of {show.Title}");

View File

@ -60,11 +60,6 @@ namespace Kyoo.Controllers
return obj;
}
protected override Task Validate(Collection ressource)
{
return Task.CompletedTask;
}
public override async Task Delete(Collection obj)
{
if (obj == null)

View File

@ -119,6 +119,8 @@ namespace Kyoo.Controllers
if (obj.ShowID <= 0)
throw new InvalidOperationException($"Can't store an episode not related to any show (showID: {obj.ShowID}).");
await base.Validate(obj);
if (obj.ExternalIDs != null)
{
foreach (MetadataID link in obj.ExternalIDs)

View File

@ -55,11 +55,6 @@ namespace Kyoo.Controllers
return obj;
}
protected override Task Validate(Genre ressource)
{
return Task.CompletedTask;
}
public override async Task Delete(Genre obj)
{
if (obj == null)

View File

@ -74,6 +74,8 @@ namespace Kyoo.Controllers
if (obj.Paths == null || !obj.Paths.Any())
throw new ArgumentException("The library should have a least one path.");
await base.Validate(obj);
if (obj.ProviderLinks != null)
foreach (ProviderLink link in obj.ProviderLinks)
link.Provider = await _providers.CreateIfNotExists(link.Provider);

View File

@ -67,6 +67,8 @@ namespace Kyoo.Controllers
protected override async Task Validate(People obj)
{
await base.Validate(obj);
if (obj.ExternalIDs != null)
foreach (MetadataID link in obj.ExternalIDs)
link.Provider = await _providers.CreateIfNotExists(link.Provider);
@ -82,14 +84,14 @@ namespace Kyoo.Controllers
foreach (MetadataID entry in obj.ExternalIDs)
_database.Entry(entry).State = EntityState.Deleted;
if (obj.Roles != null)
foreach (PeopleLink link in obj.Roles)
foreach (PeopleRole link in obj.Roles)
_database.Entry(link).State = EntityState.Deleted;
await _database.SaveChangesAsync();
}
public async Task<ICollection<PeopleLink>> GetFromShow(int showID,
Expression<Func<PeopleLink, bool>> where = null,
Sort<PeopleLink> sort = default,
public async Task<ICollection<PeopleRole>> GetFromShow(int showID,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default)
{
if (sort.Key?.Body is MemberExpression member)
@ -102,7 +104,7 @@ namespace Kyoo.Controllers
};
}
ICollection<PeopleLink> people = await ApplyFilters(_database.PeopleRoles.Where(x => x.ShowID == showID),
ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles.Where(x => x.ShowID == showID),
id => _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == id),
x => x.People.Name,
where,
@ -113,9 +115,9 @@ namespace Kyoo.Controllers
return people;
}
public async Task<ICollection<PeopleLink>> GetFromShow(string showSlug,
Expression<Func<PeopleLink, bool>> where = null,
Sort<PeopleLink> sort = default,
public async Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
Expression<Func<PeopleRole, bool>> where = null,
Sort<PeopleRole> sort = default,
Pagination limit = default)
{
if (sort.Key?.Body is MemberExpression member)
@ -128,7 +130,7 @@ namespace Kyoo.Controllers
};
}
ICollection<PeopleLink> people = await ApplyFilters(_database.PeopleRoles.Where(x => x.Show.Slug == showSlug),
ICollection<PeopleRole> people = await ApplyFilters(_database.PeopleRoles.Where(x => x.Show.Slug == showSlug),
id => _database.PeopleRoles.FirstOrDefaultAsync(x => x.ID == id),
x => x.People.Name,
where,

View File

@ -32,6 +32,8 @@ namespace Kyoo.Controllers
{
if (obj == null)
throw new ArgumentNullException(nameof(obj));
if (obj.Slug == null)
throw new ArgumentException($"Provider's slug can't be null (name: {obj.Name}).");
_database.Entry(obj).State = EntityState.Added;
@ -39,11 +41,6 @@ namespace Kyoo.Controllers
return obj;
}
protected override Task Validate(ProviderID ressource)
{
return Task.CompletedTask;
}
public override async Task Delete(ProviderID obj)
{
if (obj == null)

View File

@ -98,6 +98,8 @@ namespace Kyoo.Controllers
if (obj.ShowID <= 0)
throw new InvalidOperationException($"Can't store a season not related to any show (showID: {obj.ShowID}).");
await base.Validate(obj);
if (obj.ExternalIDs != null)
{
foreach (MetadataID link in obj.ExternalIDs)

View File

@ -97,7 +97,7 @@ namespace Kyoo.Controllers
foreach (GenreLink entry in obj.GenreLinks)
_database.Entry(entry).State = EntityState.Added;
if (obj.People != null)
foreach (PeopleLink entry in obj.People)
foreach (PeopleRole entry in obj.People)
_database.Entry(entry).State = EntityState.Added;
if (obj.ExternalIDs != null)
foreach (MetadataID entry in obj.ExternalIDs)
@ -109,6 +109,8 @@ namespace Kyoo.Controllers
protected override async Task Validate(Show obj)
{
await base.Validate(obj);
if (obj.Studio != null)
obj.Studio = await _studios.CreateIfNotExists(obj.Studio);
@ -117,7 +119,7 @@ namespace Kyoo.Controllers
link.Genre = await _genres.CreateIfNotExists(link.Genre);
if (obj.People != null)
foreach (PeopleLink link in obj.People)
foreach (PeopleRole link in obj.People)
link.People = await _people.CreateIfNotExists(link.People);
if (obj.ExternalIDs != null)
@ -157,7 +159,7 @@ namespace Kyoo.Controllers
_database.Entry(entry).State = EntityState.Deleted;
if (obj.People != null)
foreach (PeopleLink entry in obj.People)
foreach (PeopleRole entry in obj.People)
_database.Entry(entry).State = EntityState.Deleted;
if (obj.ExternalIDs != null)

View File

@ -38,11 +38,6 @@ namespace Kyoo.Controllers
return obj;
}
protected override Task Validate(Studio ressource)
{
return Task.CompletedTask;
}
public override async Task Delete(Studio obj)
{
if (obj == null)

View File

@ -57,7 +57,7 @@ namespace Kyoo.Controllers
return show;
}
public async Task<IEnumerable<PeopleLink>> Validate(IEnumerable<PeopleLink> people, bool alwaysDownload)
public async Task<IEnumerable<PeopleRole>> Validate(IEnumerable<PeopleRole> people, bool alwaysDownload)
{
if (people == null)
return null;
@ -65,7 +65,7 @@ namespace Kyoo.Controllers
string root = _config.GetValue<string>("peoplePath");
Directory.CreateDirectory(root);
foreach (PeopleLink peop in people)
foreach (PeopleRole peop in people)
{
string localPath = Path.Combine(root, peop.People.Slug + ".jpg");
if (peop.People.Poster == null)

View File

@ -69,7 +69,7 @@ namespace Kyoo
public DbSet<ProviderID> Providers { get; set; }
public DbSet<MetadataID> MetadataIds { get; set; }
public DbSet<PeopleLink> PeopleRoles { get; set; }
public DbSet<PeopleRole> PeopleRoles { get; set; }
// This is used because EF doesn't support Many-To-Many relationships so for now we need to override the getter/setters to store this.
@ -134,7 +134,7 @@ namespace Kyoo
.Ignore(x => x.Libraries)
.Ignore(x => x.Collections);
modelBuilder.Entity<PeopleLink>()
modelBuilder.Entity<PeopleRole>()
.Ignore(x => x.Slug)
.Ignore(x => x.Name)
.Ignore(x => x.Poster)

View File

@ -22,8 +22,13 @@ namespace Kyoo.Tasks
using IServiceScope serviceScope = serviceProvider.CreateScope();
IProviderRepository providers = serviceScope.ServiceProvider.GetService<IProviderRepository>();
IPluginManager pluginManager = serviceScope.ServiceProvider.GetService<IPluginManager>();
foreach (IMetadataProvider provider in pluginManager.GetPlugins<IMetadataProvider>())
{
if (string.IsNullOrEmpty(provider.Provider.Slug))
throw new ArgumentException($"Empty provider slug (name: {provider.Provider.Name}).");
await providers.CreateIfNotExists(provider.Provider);
}
}
public Task<IEnumerable<string>> GetPossibleParameters()

View File

@ -154,7 +154,7 @@ namespace Kyoo.Api
[HttpGet("{showID:int}/people")]
[Authorize(Policy = "Read")]
public async Task<ActionResult<Page<PeopleLink>>> GetPeople(int showID,
public async Task<ActionResult<Page<PeopleRole>>> GetPeople(int showID,
[FromQuery] string sortBy,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where,
@ -166,9 +166,9 @@ namespace Kyoo.Api
try
{
ICollection<PeopleLink> ressources = await _libraryManager.GetPeopleFromShow(showID,
ApiHelper.ParseWhere<PeopleLink>(where),
new Sort<PeopleLink>(sortBy),
ICollection<PeopleRole> ressources = await _libraryManager.GetPeopleFromShow(showID,
ApiHelper.ParseWhere<PeopleRole>(where),
new Sort<PeopleRole>(sortBy),
new Pagination(limit, afterID));
return Page(ressources, limit);
@ -185,7 +185,7 @@ namespace Kyoo.Api
[HttpGet("{slug}/people")]
[Authorize(Policy = "Read")]
public async Task<ActionResult<Page<PeopleLink>>> GetPeople(string slug,
public async Task<ActionResult<Page<PeopleRole>>> GetPeople(string slug,
[FromQuery] string sortBy,
[FromQuery] int afterID,
[FromQuery] Dictionary<string, string> where,
@ -197,9 +197,9 @@ namespace Kyoo.Api
try
{
ICollection<PeopleLink> ressources = await _libraryManager.GetPeopleFromShow(slug,
ApiHelper.ParseWhere<PeopleLink>(where),
new Sort<PeopleLink>(sortBy),
ICollection<PeopleRole> ressources = await _libraryManager.GetPeopleFromShow(slug,
ApiHelper.ParseWhere<PeopleRole>(where),
new Sort<PeopleRole>(sortBy),
new Pagination(limit, afterID));
return Page(ressources, limit);