mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-07-09 03:04:20 -04:00
Starting edits in a simple way
This commit is contained in:
parent
27c89ccc85
commit
2317445053
@ -17,15 +17,4 @@ namespace Kyoo.Models.Attributes
|
|||||||
RelationID = relationID;
|
RelationID = relationID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
|
||||||
public class LinkRelationAttribute : Attribute
|
|
||||||
{
|
|
||||||
public string Relation { get; }
|
|
||||||
|
|
||||||
public LinkRelationAttribute(string relation)
|
|
||||||
{
|
|
||||||
Relation = relation;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -37,8 +37,7 @@ namespace Kyoo.Models
|
|||||||
[LoadableRelation] public virtual ICollection<Collection> Collections { get; set; }
|
[LoadableRelation] public virtual ICollection<Collection> Collections { get; set; }
|
||||||
|
|
||||||
#if ENABLE_INTERNAL_LINKS
|
#if ENABLE_INTERNAL_LINKS
|
||||||
[LinkRelation(nameof(Libraries))] [SerializeIgnore]
|
[SerializeIgnore] public virtual ICollection<Link<Library, Show>> LibraryLinks { get; set; }
|
||||||
public virtual ICollection<Link<Library, Show>> LibraryLinks { get; set; }
|
|
||||||
[SerializeIgnore] public virtual ICollection<Link<Collection, Show>> CollectionLinks { get; set; }
|
[SerializeIgnore] public virtual ICollection<Link<Collection, Show>> CollectionLinks { get; set; }
|
||||||
[SerializeIgnore] public virtual ICollection<Link<Show, Genre>> GenreLinks { get; set; }
|
[SerializeIgnore] public virtual ICollection<Link<Show, Genre>> GenreLinks { get; set; }
|
||||||
#endif
|
#endif
|
||||||
|
@ -124,7 +124,7 @@ namespace Kyoo
|
|||||||
return first;
|
return first;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static T Complete<T>(T first, T second)
|
public static T Complete<T>(T first, T second, Func<PropertyInfo, bool> ignore = null)
|
||||||
{
|
{
|
||||||
if (first == null)
|
if (first == null)
|
||||||
throw new ArgumentNullException(nameof(first));
|
throw new ArgumentNullException(nameof(first));
|
||||||
@ -135,6 +135,9 @@ namespace Kyoo
|
|||||||
IEnumerable<PropertyInfo> properties = type.GetProperties()
|
IEnumerable<PropertyInfo> properties = type.GetProperties()
|
||||||
.Where(x => x.CanRead && x.CanWrite
|
.Where(x => x.CanRead && x.CanWrite
|
||||||
&& Attribute.GetCustomAttribute(x, typeof(NotMergableAttribute)) == null);
|
&& Attribute.GetCustomAttribute(x, typeof(NotMergableAttribute)) == null);
|
||||||
|
|
||||||
|
if (ignore != null)
|
||||||
|
properties = properties.Where(ignore);
|
||||||
|
|
||||||
foreach (PropertyInfo property in properties)
|
foreach (PropertyInfo property in properties)
|
||||||
{
|
{
|
||||||
@ -253,11 +256,11 @@ namespace Kyoo
|
|||||||
return types.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
|
return types.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async IAsyncEnumerable<T2> SelectAsync<T, T2>([NotNull] this IEnumerable<T> self,
|
public static async IAsyncEnumerable<T2> SelectAsync<T, T2>([CanBeNull] this IEnumerable<T> self,
|
||||||
[NotNull] Func<T, Task<T2>> mapper)
|
[NotNull] Func<T, Task<T2>> mapper)
|
||||||
{
|
{
|
||||||
if (self == null)
|
if (self == null)
|
||||||
throw new ArgumentNullException(nameof(self));
|
yield break;
|
||||||
if (mapper == null)
|
if (mapper == null)
|
||||||
throw new ArgumentNullException(nameof(mapper));
|
throw new ArgumentNullException(nameof(mapper));
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
@ -10,8 +9,6 @@ using Kyoo.Models;
|
|||||||
using Kyoo.Models.Attributes;
|
using Kyoo.Models.Attributes;
|
||||||
using Kyoo.Models.Exceptions;
|
using Kyoo.Models.Exceptions;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
|
|
||||||
namespace Kyoo.Controllers
|
namespace Kyoo.Controllers
|
||||||
{
|
{
|
||||||
@ -155,7 +152,6 @@ namespace Kyoo.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
||||||
@ -170,83 +166,11 @@ namespace Kyoo.Controllers
|
|||||||
if (old == null)
|
if (old == null)
|
||||||
throw new ItemNotFound($"No resource found with the ID {edited.ID}.");
|
throw new ItemNotFound($"No resource found with the ID {edited.ID}.");
|
||||||
|
|
||||||
List<PropertyInfo> navigations = typeof(T).GetProperties()
|
|
||||||
.Where(x => x.GetCustomAttribute<EditableRelationAttribute>() != null)
|
await EditRelations(old, edited);
|
||||||
.ToList();
|
|
||||||
List<string> links = typeof(T).GetProperties()
|
|
||||||
.Select(x => x.GetCustomAttribute<LinkRelationAttribute>()?.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<IResource> oValues = (relation.GetValue(old) as IEnumerable)!.Cast<IResource>();
|
|
||||||
List<IResource> nValues = (relation.GetValue(edited) as IEnumerable)?.Cast<IResource>().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<EditableRelationAttribute>() == null)
|
|
||||||
{
|
|
||||||
navigation.SetValue(edited, default);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (resetOld)
|
if (resetOld)
|
||||||
Utility.Nullify(old);
|
Utility.Nullify(old);
|
||||||
Utility.Complete(old, edited);
|
Utility.Complete(old, edited, x => x.GetCustomAttribute<EditableRelationAttribute>() != null);
|
||||||
await Validate(old);
|
|
||||||
await Database.SaveChangesAsync();
|
await Database.SaveChangesAsync();
|
||||||
return old;
|
return old;
|
||||||
}
|
}
|
||||||
@ -255,13 +179,13 @@ namespace Kyoo.Controllers
|
|||||||
Database.ChangeTracker.LazyLoadingEnabled = lazyLoading;
|
Database.ChangeTracker.LazyLoadingEnabled = lazyLoading;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected bool ShouldValidate<T2>(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))
|
if (string.IsNullOrEmpty(resource.Slug))
|
||||||
throw new ArgumentException("Resource can't have null as a slug.");
|
throw new ArgumentException("Resource can't have null as a slug.");
|
||||||
@ -282,7 +206,7 @@ namespace Kyoo.Controllers
|
|||||||
}
|
}
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual async Task Delete(int id)
|
public virtual async Task Delete(int id)
|
||||||
{
|
{
|
||||||
T resource = await Get(id);
|
T resource = await Get(id);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
@ -153,25 +154,31 @@ namespace Kyoo.Controllers
|
|||||||
{
|
{
|
||||||
await base.Create(obj);
|
await base.Create(obj);
|
||||||
_database.Entry(obj).State = EntityState.Added;
|
_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.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).");
|
await _database.SaveChangesAsync($"Trying to insert a duplicated episode (slug {obj.Slug} already exists).");
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override async Task Validate(Episode resource)
|
protected override async Task EditRelations(Episode resource, Episode changed)
|
||||||
{
|
{
|
||||||
if (resource.ShowID <= 0)
|
if (resource.ShowID <= 0)
|
||||||
throw new InvalidOperationException($"Can't store an episode not related to any show (showID: {resource.ShowID}).");
|
throw new InvalidOperationException($"Can't store an episode not related to any show (showID: {resource.ShowID}).");
|
||||||
|
|
||||||
await base.Validate(resource);
|
await base.EditRelations(resource, changed);
|
||||||
|
|
||||||
// if (resource.Tracks != null)
|
ICollection<Track> oldTracks = resource.Tracks;
|
||||||
// {
|
resource.Tracks = await changed.Tracks.SelectAsync(async track =>
|
||||||
// resource.Tracks = await resource.Tracks
|
{
|
||||||
// .SelectAsync(x => _tracks.CreateIfNotExists(x, true))
|
Track oldValue = oldTracks?.FirstOrDefault(x => Utility.ResourceEquals(track, x));
|
||||||
// .ToListAsync();
|
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)
|
if (resource.ExternalIDs != null)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user