From 2317445053ad813c2b128ebe19684047fae6c971 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Tue, 16 Mar 2021 02:33:31 +0100 Subject: [PATCH] Starting edits in a simple way --- .../Models/Attributes/RelationAttributes.cs | 11 --- Kyoo.Common/Models/Resources/Show.cs | 3 +- Kyoo.Common/Utility.cs | 9 +- Kyoo.CommonAPI/LocalRepository.cs | 94 ++----------------- .../Repositories/EpisodeRepository.cs | 27 ++++-- 5 files changed, 33 insertions(+), 111 deletions(-) diff --git a/Kyoo.Common/Models/Attributes/RelationAttributes.cs b/Kyoo.Common/Models/Attributes/RelationAttributes.cs index fedfc6db..aac0e633 100644 --- a/Kyoo.Common/Models/Attributes/RelationAttributes.cs +++ b/Kyoo.Common/Models/Attributes/RelationAttributes.cs @@ -17,15 +17,4 @@ namespace Kyoo.Models.Attributes RelationID = relationID; } } - - [AttributeUsage(AttributeTargets.Property)] - public class LinkRelationAttribute : Attribute - { - public string Relation { get; } - - public LinkRelationAttribute(string relation) - { - Relation = relation; - } - } } \ No newline at end of file diff --git a/Kyoo.Common/Models/Resources/Show.cs b/Kyoo.Common/Models/Resources/Show.cs index f77bfc2e..7b948b55 100644 --- a/Kyoo.Common/Models/Resources/Show.cs +++ b/Kyoo.Common/Models/Resources/Show.cs @@ -37,8 +37,7 @@ namespace Kyoo.Models [LoadableRelation] public virtual ICollection Collections { get; set; } #if ENABLE_INTERNAL_LINKS - [LinkRelation(nameof(Libraries))] [SerializeIgnore] - public virtual ICollection> LibraryLinks { get; set; } + [SerializeIgnore] public virtual ICollection> LibraryLinks { get; set; } [SerializeIgnore] public virtual ICollection> CollectionLinks { get; set; } [SerializeIgnore] public virtual ICollection> GenreLinks { get; set; } #endif diff --git a/Kyoo.Common/Utility.cs b/Kyoo.Common/Utility.cs index 1504abf5..d7be7dec 100644 --- a/Kyoo.Common/Utility.cs +++ b/Kyoo.Common/Utility.cs @@ -124,7 +124,7 @@ namespace Kyoo return first; } - public static T Complete(T first, T second) + public static T Complete(T first, T second, Func ignore = null) { if (first == null) throw new ArgumentNullException(nameof(first)); @@ -135,6 +135,9 @@ namespace Kyoo IEnumerable properties = type.GetProperties() .Where(x => x.CanRead && x.CanWrite && Attribute.GetCustomAttribute(x, typeof(NotMergableAttribute)) == null); + + if (ignore != null) + properties = properties.Where(ignore); foreach (PropertyInfo property in properties) { @@ -253,11 +256,11 @@ namespace Kyoo return types.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType); } - public static async IAsyncEnumerable SelectAsync([NotNull] this IEnumerable self, + public static async IAsyncEnumerable SelectAsync([CanBeNull] this IEnumerable self, [NotNull] Func> mapper) { if (self == null) - throw new ArgumentNullException(nameof(self)); + yield break; if (mapper == null) throw new ArgumentNullException(nameof(mapper)); diff --git a/Kyoo.CommonAPI/LocalRepository.cs b/Kyoo.CommonAPI/LocalRepository.cs index 489f9742..b35bb041 100644 --- a/Kyoo.CommonAPI/LocalRepository.cs +++ b/Kyoo.CommonAPI/LocalRepository.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -10,8 +9,6 @@ using Kyoo.Models; using Kyoo.Models.Attributes; using Kyoo.Models.Exceptions; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; -using Microsoft.EntityFrameworkCore.Metadata; namespace Kyoo.Controllers { @@ -155,7 +152,6 @@ namespace Kyoo.Controllers } } - public virtual async Task Edit(T edited, bool resetOld) { if (edited == null) @@ -170,83 +166,11 @@ namespace Kyoo.Controllers if (old == null) throw new ItemNotFound($"No resource found with the ID {edited.ID}."); - List navigations = typeof(T).GetProperties() - .Where(x => x.GetCustomAttribute() != null) - .ToList(); - List links = typeof(T).GetProperties() - .Select(x => x.GetCustomAttribute()?.Relation) - .Where(x => x != null) - .ToList(); - - // This handle every links so every Many to Many - // TODO should handle non links value (One to Many) - foreach (string relationName in links) - { - PropertyInfo relation = navigations.Find(x => x.Name == relationName); - navigations.Remove(relation); - - if (relation!.GetValue(edited) == null && !resetOld) - continue; - - await _library.Load(old, relation.Name); - IEnumerable oValues = (relation.GetValue(old) as IEnumerable)!.Cast(); - List nValues = (relation.GetValue(edited) as IEnumerable)?.Cast().ToList(); - - foreach (IResource x in oValues) - { - int nIndex = nValues?.FindIndex(y => Utility.ResourceEquals(x, y)) ?? -1; - if (nIndex == -1 || resetOld) - await _linkManager.Delete(old, x); - else - nValues!.RemoveAt(nIndex); - } - - await nValues.ForEachAsync(x => _linkManager.Add(old, x)); - } - - // This handle every X to One - foreach (PropertyInfo relation in navigations) - { - IResource oValues = relation.GetValue(old) as IResource; - IResource nValues = relation.GetValue(edited) as IResource; - - if (nValues == null && !resetOld) - continue; - - if (false) // TODO change if false with if relation is casquade delete - await _library.Delete(oValue); - relation.SetValue(old, nValues); - // TODO call create if not exist - // TODO change relationID to the new value ID. - } - - - // This handle every One to Many or Many to One - foreach (PropertyInfo navigation in navigations) - { - if (resetOld || navigation.GetValue(edited) != default) - { - // TODO only works for X To One and not X to Many - await _library.Value.Load(old, navigation.Name); - object value = navigation.GetValue(old); - if (value is IEnumerable list) // TODO handle externalIds & PeopleRoles (implement Link<> for those) - list.ForEach(x => _library.Value.Delete(x)); - else if (value is IResource resource) - _library.Value.Delete(resource); - } - - - if (navigation.GetCustomAttribute() == null) - { - navigation.SetValue(edited, default); - continue; - } - } - + + await EditRelations(old, edited); if (resetOld) Utility.Nullify(old); - Utility.Complete(old, edited); - await Validate(old); + Utility.Complete(old, edited, x => x.GetCustomAttribute() != null); await Database.SaveChangesAsync(); return old; } @@ -255,13 +179,13 @@ namespace Kyoo.Controllers Database.ChangeTracker.LazyLoadingEnabled = lazyLoading; } } - - protected bool ShouldValidate(T2 value) + + protected virtual Task EditRelations(T resource, T newValues) { - return value != null && Database.Entry(value).State == EntityState.Detached; + return Validate(resource); } - - protected virtual Task Validate(T resource) + + private Task Validate(T resource) { if (string.IsNullOrEmpty(resource.Slug)) throw new ArgumentException("Resource can't have null as a slug."); @@ -282,7 +206,7 @@ namespace Kyoo.Controllers } return Task.CompletedTask; } - + public virtual async Task Delete(int id) { T resource = await Get(id); diff --git a/Kyoo/Controllers/Repositories/EpisodeRepository.cs b/Kyoo/Controllers/Repositories/EpisodeRepository.cs index ee53f331..9a25a2b1 100644 --- a/Kyoo/Controllers/Repositories/EpisodeRepository.cs +++ b/Kyoo/Controllers/Repositories/EpisodeRepository.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -153,25 +154,31 @@ namespace Kyoo.Controllers { await base.Create(obj); _database.Entry(obj).State = EntityState.Added; + obj.Tracks = await obj.Tracks.SelectAsync(x => _tracks.CreateIfNotExists(x, true)).ToListAsync(); obj.ExternalIDs.ForEach(x => _database.Entry(x).State = EntityState.Added); - obj.Tracks.ForEach(x => _database.Entry(x).State = EntityState.Added); await _database.SaveChangesAsync($"Trying to insert a duplicated episode (slug {obj.Slug} already exists)."); return obj; } - protected override async Task Validate(Episode resource) + protected override async Task EditRelations(Episode resource, Episode changed) { if (resource.ShowID <= 0) throw new InvalidOperationException($"Can't store an episode not related to any show (showID: {resource.ShowID})."); - await base.Validate(resource); - - // if (resource.Tracks != null) - // { - // resource.Tracks = await resource.Tracks - // .SelectAsync(x => _tracks.CreateIfNotExists(x, true)) - // .ToListAsync(); - // } + await base.EditRelations(resource, changed); + + ICollection oldTracks = resource.Tracks; + resource.Tracks = await changed.Tracks.SelectAsync(async track => + { + Track oldValue = oldTracks?.FirstOrDefault(x => Utility.ResourceEquals(track, x)); + if (oldValue == null) + return await _tracks.CreateIfNotExists(track, true); + oldTracks.Remove(oldValue); + return oldValue; + }) + .ToListAsync(); + foreach (Track x in oldTracks) + await _tracks.Delete(x); if (resource.ExternalIDs != null) {