6
votes

La collection dans le modèle de structure d'entité ne se met pas à jour

J'ai quelques modèles simples. Un modèle PlaylistEntry et un modèle Playlist qui contient une collection de modèles PlaylistEntry.

Playlist:

    var playlist = new Playlist { Name = "My first playlist" };
    using (var context = new MusicPlayerContext(stringBuilder))
    {
        context.Playlists.Add(playlist);
        context.SaveChanges();
    }

    playlist.PlaylistEntries.Add(new PlaylistEntry { FilePath = "lkdfj", PlaylistId = playlist.PlaylistId });

    using (var context = new MusicPlayerContext(stringBuilder))
    {
        context.Playlists.Attach(playlist);
        context.Entry(playlist).State = EntityState.Modified;
        context.SaveChanges();
    }

    using (var context = new MusicPlayerContext(stringBuilder))
    {
        context.Playlists.Include(x => x.PlaylistEntries).ToList();
    }

PlaylistEntry

    public class PlaylistEntry
    {
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int PlaylistEntryId { get; set; }

        public string FilePath { get; set; }

        [ForeignKey("Playlist")]
        public int PlaylistId { get; set; }

        [Required]
        public Playlist Playlist { get; set; }
    }

Lorsque j'ajoute une PlaylistEntry à une Playlist dans un état détaché, je m'attends à ce que lorsque j'attache et enregistre les modifications, les modifications apportées à la liste PlaylistEntries soient également enregistrées. Ils ne sont pas enregistrés et la liste est vide.

Voici mon code:

    public class Playlist
    {
        private ICollection<PlaylistEntry> playlistEntries;

        public int PlaylistId { get; set; }

        public string Name { get; set; }

        public virtual ICollection<PlaylistEntry> PlaylistEntries
        {
            get => this.playlistEntries ?? (this.playlistEntries = new HashSet<PlaylistEntry>());
            set => this.playlistEntries = value;
        }
    }

Ce dernier appel ToList renvoie la playlist que j'ai créée mais la collection PlaylistEntries dessus est vide.

Quelqu'un peut-il repérer ce que je fais mal ou suggérer une solution différente s'il vous plaît? J'ai passé environ 7 heures là-dessus et je n'arrive pas à comprendre toute ma vie.


2 commentaires

C'est juste une supposition, mais avez-vous inspecté Entry pour l'objet PlaylistEntry ?


Je me demande si vous devez attacher ces objets à context.PlaylistEntries , en supposant que cela existe.


3 Réponses :


0
votes

Chaque fois que vous appelez context.SaveChanges (); juste après avoir ajouté uniquement PlayList sans PlaylistEntries , Entity Framework envoie la commande à la base de données et réinitialiser l'état du contexte. Donc, après cela, ajouter PlaylistEntries à la PlayList ne ferait aucun effet. Pour insérer PlaylistEntries avec Playlist , écrivez votre code comme suit:

var playlist = new Playlist { Name = "My first playlist" };
using (var context = new MusicPlayerContext(stringBuilder))
{
    playlist.PlaylistEntries.Add(new PlaylistEntry { FilePath = "lkdfj", PlaylistId = playlist.PlaylistId }); // Even you don't need to specify `PlaylistId` here. EF can map it.
    context.Playlists.Add(playlist);
    context.SaveChanges();
}

J'espère que cela fonctionnera pour vous.

p >


0 commentaires

3
votes

La fonctionnalité qui vous manque est le fonctionnement du cache de contexte. Lorsque vous attachez une entité (manuellement ou en demandant une entité à la base de données) au contexte, elle est surveillée par le framework d'entité pour les changements et le suivi de ces changements.

Vous avez ajouté un objet à la propriété d'une entité, puis l'ajoute au contexte. Entity Framework le surveille maintenant pour les modifications nécessaires pour effectuer un travail supplémentaire. Tous les objets ajoutés de cette manière sont marqués comme inchangés . Marquer le parent modifié ne marque pas les propriétés de navigation comme changées.

Vous pouvez soit ajouter l'entité à la propriété dans le contexte:

using (var context = new MusicPlayerContext())
{
    Console.WriteLine("Save PlayList 2 (full attach)");
    context.Playlists.Attach(playlist2);
    context.Entry(playlist2).State = EntityState.Modified;
    context.Entry(playlist2.PlaylistEntries.First()).State = EntityState.Added;
    context.SaveChanges();
}

... OU marquez les entrées des deux objets après avoir attaché le parent.

using (var context = new MusicPlayerContext())
{
    Console.WriteLine("Save PlayList 2 (in context add)");
    context.Playlists.Attach(playlist3);
    playlist3.PlaylistEntries.Add(new PlaylistEntry { 
      FilePath = "Entry3", 
      PlaylistId = playlist3.PlaylistId, PlaylistEntryId = 3 
    });
    context.SaveChanges();
}

Dans les deux cas, Entity Framework sait maintenant quoi faire dans la base de données lorsque SaveChanges () se produit.

DotNetFiddle


3 commentaires

Merci beaucoup! Les deux solutions ont fonctionné. J'ai décidé de suivre la première approche car elle semble rendre les choses beaucoup moins compliquées. Pour la deuxième solution, j'ai dû modifier un peu le code pour travailler avec plusieurs nouvelles entrées. Mise à jour du message


Pour votre première méthode, existe-t-il un moyen de générer automatiquement le PlaylistEntryId? Si je ne donne pas explicitement un ID, j'obtiens l'exception Une valeur nulle générée par le magasin a été renvoyée pour un membre non nullable 'PlaylistEntryId' de type 'MusicPlayer.Data.DAL.PlaylistEntry'.


La réponse à cela est que mon schéma était obsolète. J'ai supprimé et recréé la base de données qui a résolu le problème



0
votes

J'ai décidé d'ajouter de nouvelles entrées à la liste à partir du contexte, comme les autres réponses l'ont suggéré. Si quelqu'un veut toujours savoir comment faire fonctionner cela dans un état détaché, voici comment:

var playlist = new Playlist { Name = "My first playlist" };

using (var context = new MusicPlayerContext(stringBuilder))
{
    context.Playlists.Add(playlist);
}

playlist.PlaylistEntries.Add(new PlaylistEntry { FilePath = "Song7" });
playlist.PlaylistEntries.Add(new PlaylistEntry { FilePath = "Song8" });

using (var context = new MusicPlayerContext(stringBuilder))
{
    context.Playlists.Add(playlist);
    context.Entry(playlist).State = EntityState.Modified;

    foreach (var entry in playlist.PlaylistEntries)
    {
        context.Entry(entry).State = entry.PlaylistEntryId == 0 ? EntityState.Added : EntityState.Modified;
    }

    context.SaveChanges();
}

using (var context = new MusicPlayerContext(stringBuilder))
{
    context.Playlists.Include(x => x.PlaylistEntries).ToList();
}

Le context.Playlists.Add (playlist) ne le fait pas réellement ajoutez une nouvelle liste de lecture. D'après ce que je comprends, il joint la liste de lecture et toutes les nouvelles entrées. Si je n'utilise pas Add, j'obtiens une erreur car le PlaylistEntryID des nouvelles entrées est défini sur 0 par défaut.


0 commentaires