¿Cómo actualizar la relación muchos a muchos en Entity Framework 7?

asp.net entity-framework entity-framework-core

Pregunta

tl; dr: Tengo una base de datos con una relación de muchos a muchos. Cuando edito sus datos y llamo a SaveChanges () no se cambia nada en la base de datos.

Pregunta completa:

Me encontré con un problema muy extraño. Quiero actualizar la relación de muchos a muchos pero no quiere cambiar. Mis modelos se ven así (solo he publicado esta parte que es importante en esta pregunta):

public class Note
    {
        public Note()
        {
            NoteTags = new HashSet<NoteTag>()
        }

        public int NoteId { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

public class NoteTag
    {
        public int NoteId { get; set; }
        public Note Note { get; set; }

        public int TagId { get; set; }
        public Tag Tag { get; set; }
    }

public class Tag
    {
        public Tag()
        {
            NoteTags = new HashSet<NoteTag>();
        }

        public int TagId { get; set; }
        public string Name { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

Lo que hago para actualizar esta relación db:

Obtener el estado actual de la nota de la base de datos:

public class Note
    {
        public Note()
        {
            NoteTags = new HashSet<NoteTag>()
        }

        public int NoteId { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

public class NoteTag
    {
        public int NoteId { get; set; }
        public Note Note { get; set; }

        public int TagId { get; set; }
        public Tag Tag { get; set; }
    }

public class Tag
    {
        public Tag()
        {
            NoteTags = new HashSet<NoteTag>();
        }

        public int TagId { get; set; }
        public string Name { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

Borrar lista de notas y añadir una nueva:

public class Note
    {
        public Note()
        {
            NoteTags = new HashSet<NoteTag>()
        }

        public int NoteId { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

public class NoteTag
    {
        public int NoteId { get; set; }
        public Note Note { get; set; }

        public int TagId { get; set; }
        public Tag Tag { get; set; }
    }

public class Tag
    {
        public Tag()
        {
            NoteTags = new HashSet<NoteTag>();
        }

        public int TagId { get; set; }
        public string Name { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

Lo que hace TagsToAddToTags: encuentra las etiquetas adecuadas en la base de datos y las agrega a la Lista <> así:

public class Note
    {
        public Note()
        {
            NoteTags = new HashSet<NoteTag>()
        }

        public int NoteId { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

public class NoteTag
    {
        public int NoteId { get; set; }
        public Note Note { get; set; }

        public int TagId { get; set; }
        public Tag Tag { get; set; }
    }

public class Tag
    {
        public Tag()
        {
            NoteTags = new HashSet<NoteTag>();
        }

        public int TagId { get; set; }
        public string Name { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

Luego le digo a la base de datos que he modificado algunos datos y los guardo:

public class Note
    {
        public Note()
        {
            NoteTags = new HashSet<NoteTag>()
        }

        public int NoteId { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

public class NoteTag
    {
        public int NoteId { get; set; }
        public Note Note { get; set; }

        public int TagId { get; set; }
        public Tag Tag { get; set; }
    }

public class Tag
    {
        public Tag()
        {
            NoteTags = new HashSet<NoteTag>();
        }

        public int TagId { get; set; }
        public string Name { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

La variable noteInDb tiene exactamente el aspecto que deseo que tenga cuando la guarde en la base de datos. Desafortunadamente, no se está guardando nada, cuando veo la base de datos, se ve igual que antes.

El hecho extraño también es que cuando escribo esta línea justo después de SaveChanges ():

public class Note
    {
        public Note()
        {
            NoteTags = new HashSet<NoteTag>()
        }

        public int NoteId { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

public class NoteTag
    {
        public int NoteId { get; set; }
        public Note Note { get; set; }

        public int TagId { get; set; }
        public Tag Tag { get; set; }
    }

public class Tag
    {
        public Tag()
        {
            NoteTags = new HashSet<NoteTag>();
        }

        public int TagId { get; set; }
        public string Name { get; set; }
        public virtual ICollection<NoteTag> NoteTags { get; set; }
        [...]
    }

Puedo ver que noteInDb2 tiene 9 NoteTags (tiene 4 de ellos antes y quería que tuviera 5, así que esta es la suma de lo que era antes y lo que quería agregar). Pero cuando voy a LINQPad para ver cómo se ve realmente, me muestra 4 NoteTags antiguos (sin cambios).

¿Qué estoy haciendo mal?

Respuesta popular

Puede usar esta extensión que hice para eliminar los no seleccionados y agregar los recién seleccionados a la lista

    public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
    {
        db.Set<T>().RemoveRange(currentItems.Except(newItems, getKey));
        db.Set<T>().AddRange(newItems.Except(currentItems, getKey));
    }

    public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
    {
        return items
            .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
            .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
            .Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
            .Select(t => t.t.item);
    }

usándolo se ve así

    public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
    {
        db.Set<T>().RemoveRange(currentItems.Except(newItems, getKey));
        db.Set<T>().AddRange(newItems.Except(currentItems, getKey));
    }

    public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
    {
        return items
            .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
            .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
            .Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
            .Select(t => t.t.item);
    }



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué