Trying things with a generic edit function with repositories

This commit is contained in:
Zoe Roux 2021-03-16 01:41:26 +01:00
parent b84baae99a
commit 27c89ccc85
5 changed files with 86 additions and 40 deletions

View File

@ -17,4 +17,15 @@ namespace Kyoo.Models.Attributes
RelationID = relationID;
}
}
[AttributeUsage(AttributeTargets.Property)]
public class LinkRelationAttribute : Attribute
{
public string Relation { get; }
public LinkRelationAttribute(string relation)
{
Relation = relation;
}
}
}

View File

@ -37,7 +37,8 @@ namespace Kyoo.Models
[LoadableRelation] public virtual ICollection<Collection> Collections { get; set; }
#if ENABLE_INTERNAL_LINKS
[SerializeIgnore] public virtual ICollection<Link<Library, Show>> LibraryLinks { get; set; }
[LinkRelation(nameof(Libraries))] [SerializeIgnore]
public virtual ICollection<Link<Library, Show>> LibraryLinks { get; set; }
[SerializeIgnore] public virtual ICollection<Link<Collection, Show>> CollectionLinks { get; set; }
[SerializeIgnore] public virtual ICollection<Link<Show, Genre>> GenreLinks { get; set; }
#endif

View File

@ -132,7 +132,7 @@ namespace Kyoo.Controllers
public void SetValue(object target, object value)
{
throw new NotImplementedException();
// Values are ignored and should not be editable, except if the internal value is set.
}
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
@ -154,6 +155,7 @@ namespace Kyoo.Controllers
}
}
public virtual async Task<T> Edit(T edited, bool resetOld)
{
if (edited == null)
@ -168,47 +170,77 @@ namespace Kyoo.Controllers
if (old == null)
throw new ItemNotFound($"No resource found with the ID {edited.ID}.");
// foreach (PropertyInfo navigation in typeof(T).GetProperties()
// .Where(x => x.GetCustomAttribute<LoadableRelationAttribute>() != null))
// {
// if (navigation.GetCustomAttribute<EditableRelationAttribute>() == null)
// {
// navigation.SetValue(edited, default);
// continue;
// }
//
// 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
// list.ForEach(x => _library.Value.Delete(x));
// else if (value is IResource resource)
// _library.Value.Delete(resource);
// }
// }
foreach (NavigationEntry navigation in Database.Entry(old).Navigations)
{
if (navigation.Metadata.PropertyInfo.GetCustomAttribute<EditableRelationAttribute>() != null)
{
if (resetOld)
{
await navigation.LoadAsync();
continue;
}
IClrPropertyGetter getter = navigation.Metadata.GetGetter();
List<PropertyInfo> navigations = typeof(T).GetProperties()
.Where(x => x.GetCustomAttribute<EditableRelationAttribute>() != null)
.ToList();
List<string> links = typeof(T).GetProperties()
.Select(x => x.GetCustomAttribute<LinkRelationAttribute>()?.Relation)
.Where(x => x != null)
.ToList();
if (getter.HasDefaultValue(edited))
// 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 navigation.LoadAsync();
// TODO this may be usless for lists since the API does not return IDs but the
// TODO LinkEquality does not check slugs (their are lazy loaded and only the ID is available)
// if (Utility.ResourceEquals(getter.GetClrValue(edited), getter.GetClrValue(old)))
// navigation.Metadata.PropertyInfo.SetValue(edited, default);
}
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
navigation.Metadata.PropertyInfo.SetValue(edited, default);
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)

View File

@ -15,6 +15,7 @@ namespace Kyoo
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
{
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
ChangeTracker.LazyLoadingEnabled = false;
}
public DbSet<Library> Libraries { get; set; }
@ -40,6 +41,7 @@ namespace Kyoo
NpgsqlConnection.GlobalTypeMapper.MapEnum<StreamType>();
ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;
ChangeTracker.LazyLoadingEnabled = false;
}
protected override void OnModelCreating(ModelBuilder modelBuilder)