3
votes

Suppression d'un élément d'une collection dans EF - problème étrange

Je travaille sur un projet de cadre d'entité dans lequel j'ai du mal à supprimer un élément de l'une de mes collections. J'ai d'abord une relation "One to Many" créée entre mon objet "Resto" et "OpeningTimes" de la manière suivante: Dans mon modèle:

   public async Task<ActionResult> RemoveSlotTimeToRestaurant(int RestoId, int SlotId)
    {
        var resto = await DbManager.Restos.FirstAsync(r => r.Id == RestoId);
        if (resto != null)
        {
            SlotTime slotTime = resto.OpeningTimes.FirstOrDefault(m => m.SlotTimeId == SlotId);

            if(slotTime != null)
            {
                resto.OpeningTimes.Remove(slotTime);
                await DbManager.SaveChangesAsync();
                return RedirectToAction("edit", new { id = RestoId });
            }
        }
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

Depuis que j'ai une autre relation avec cet objet (voir celle d'Applications User), j'utilise également le langage Fluent pour supprimer les ambiguïtés.

De la manière suivante:

        [HttpPost]
    public async Task<ActionResult> AddSlotTimeToRestaurant(AddSlotTimeToRestaurantView model)
    {
        var resto = await DbManager.Restos.FirstAsync(r => r.Id == model.RestoId);
        if(resto != null)
        {
            if(model.OpenTimeId < model.CloseTimeId)
            {
                SlotTime slotTime = new SlotTime()
                {
                    DayOfWeek = model.Day,
                    OpenTime = model.SlotTimeList.timeSpanViews.FirstOrDefault(m => m.Id == model.OpenTimeId).TimeSpan,
                    CloseTime = model.SlotTimeList.timeSpanViews.FirstOrDefault(m => m.Id == model.CloseTimeId).TimeSpan
                };
                resto.OpeningTimes.Add(slotTime);
                await DbManager.SaveChangesAsync();
                return RedirectToAction("edit", new { id = model.RestoId });
            }
            else
            {
                ModelState.AddModelError("SelectedSlotTimeId_1_Stop", "L'heure de fermeture doit être après l'heure d'ouverture");
                return View();
            }               
        }
        return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
    }

Mais ces relations fonctionnent très bien.

Aujourd'hui, je fais des opérations de base sur mes "Heures d'ouverture" dans mon contrôleur en ajoutant un élément dans la base de données avec ce qui suit:

        modelBuilder.Entity<Resto>()
                    .HasMany<ApplicationUser>(s => s.Administrators)
                    .WithMany(c => c.Resto_Admin)
                    .Map(cs =>
                    {
                        cs.MapLeftKey("Resto_Admin");
                        cs.MapRightKey("Admin");
                        cs.ToTable("RestosAdmins");
                    });

        modelBuilder.Entity<Resto>()
        .HasMany<ApplicationUser>(s => s.Chefs)
        .WithMany(c => c.Resto_Chefs)
        .Map(cs =>
        {
            cs.MapLeftKey("Resto_Chefs");
            cs.MapRightKey("Chef");
            cs.ToTable("RestosChefs");
        });
modelBuilder.Entity<Resto>()
                    .HasOptional(s => s.Menu) 
                    .WithRequired(ad => ad.resto)
                    .WillCascadeOnDelete(true); 

Ce code fonctionne comme prévu. Mais maintenant, j'essaye de faire une autre fonction pour supprimer l'objet avec le code suivant:

 [Table("Resto")]
public class Resto
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string PhoneNumber { get; set; }
    public string Address { get; set; }   //TODO Ajouter adresse détaillées 

    public virtual ICollection<SlotTime> OpeningTimes {get; set;}




    public virtual ICollection<ApplicationUser> Administrators { get; set; }
    public virtual ICollection<ApplicationUser> Chefs { get; set; }

    public virtual Menu Menu {get; set;}
}
[Table("RestoSlotTimes")]

public class SlotTime
{
    public int SlotTimeId { get; set; }
    public DayOfWeek DayOfWeek { get; set; }
    public TimeSpan OpenTime { get; set; }
    public TimeSpan CloseTime { get; set; }


    public int RestoId { get; set; }
    public virtual Resto Resto { get; set; }
}

J'ai une erreur sur la ligne DbSave ... Où cela ressemble à EF essayez non pas pour supprimer mon objet mais juste pour le conserver et définir son identifiant sur Null ... Ce n'est pas ce que j'attends

L'opération a échoué: la relation n'a pas pu être modifiée car une ou plusieurs propriétés de clé étrangère ne peuvent pas être Null. Lorsqu'une modification est apportée à une relation, la propriété de clé étrangère associée est définie sur une valeur nulle. Si la clé étrangère ne prend pas en charge les valeurs nulles, une nouvelle relation doit être définie, la propriété de clé étrangère doit se voir attribuer une autre valeur non nulle, ou l'objet non lié doit être supprimé

J'ai le sentiment que ma relation est bien configurée pour EF. J'ai envie de faire correctement la relation d'ajout et de suppression ... Pensez-vous que cela peut provenir de la configuration fluide?

BTW une fois que j'ai supprimé le "Resto", la suppression en cascade fonctionne bien où tous mes éléments "OpeningTimes" sont bien supprimés de la base de données sans erreur.

Meilleures salutations,


0 commentaires

4 Réponses :


1
votes

Pouvez-vous essayer comme ci-dessous:

DbManager.Entry(slotTime).State = EntityState.Deleted;
await DbManager.SaveChangesAsync();


2 commentaires

Bonjour, Oui, comme ça ça marche ... On dirait qu'EF ne comprend pas "Supprimer" comme action "Supprimer" ;-)


EF comprendra supprimer comme supprimant cette ligne et il ne considérera pas la règle de cascade, donc il rendra nul dans la table parent en supprimant l'enregistrement de la table enfant. Si la règle de cascade est correctement définie et si vous souhaitez supprimer les moyens basés sur la règle de cascade, vous devez fournir les instructions ci-dessus. Cela s'applique aux scénarios de relation un-à-plusieurs.



4
votes

Si vous souhaitez pouvoir modifier l'entité enfant ( OpeningTimes ) via l'entité parente ( Resto ), vous devez spécifier la relation entre les deux dans le constructeur du modèle:

DbManager.SlotTimes.Remove(slotTime);

Ce billet de blog donne plus de détails sur la gestion de ce scénario.


Une autre approche consisterait simplement à supprimer l'entité directement du DbSet défini sur le DbContext: p>

modelBuilder.Entity<Resto>()
    .HasMany(r => r.OpeningTimes) 
    .WithOptional()
    .HasForeignKey(ot => ot.RestoId)


4 commentaires

Bonjour devNull. J'ai essayé comme vous proposiez l'approche fluide mais le problème est toujours le même ... Même message d'erreur.


Et pour la deuxième approche, je n'ai pas spécifié slotTime dans le DbSet. Je n'y accède que via Resto depuis ses Childs ...


Oui, enfin, votre deuxième approche semble être la bonne. Trouvé enfin ceci ici: weblogs.asp.net/zeeshanhirani/...


BTS comme je l'ai dit ci-dessus "Heures d'ouverture" n'est pas disponible via mon DbManager ... Je crois parce que non défini dans le DBSet ... Dois-je le déclarer ici? Est-ce la meilleure façon de déclarer les enfants dans le DbSet?



1
votes

Il me semble que vous devez ajouter un autre appel fluide, pour définir comment les SlotTimes se rapportent au Resto .

D'après ce que vous décrivez, le SlotTime dépend entièrement du Resto : vous ne pouvez pas avoir un SlotTime qui n'est pas t associé à un Resto . Mais, de toute évidence, EF ne peut pas simplement déduire ceci: vous allez devoir le dire, avec une configuration fluide.

Je ne peux pas recréer cela de mémoire car c'est aussi compliqué, et je ne suis pas sur une machine avec VS installé. Mais je pense que vous commenceriez par ceci:

  modelBuilder.Entity<Resto>()
    .HasMany<TimeSlot>(r => r.OpeningTimes)
    .WithOne(t => t.Resto)
    ....

Je m'excuse de ne pas pouvoir répondre à l'appel pour vous.

Au fait, cette autre question a une très bonne réponse qui explique tout: EF 4: la suppression d'un objet enfant de la collection ne le supprime pas - pourquoi?


2 commentaires

Merci Ann, en effet votre approche ne fonctionnera pas (comme celle devNull). Mais pourrait trouver la réponse plus loin dans le lien que vous envoyez. L'explication complète est disponible ici weblogs.asp.net/zeeshanhirani /…


@JonathanHalleux Heureux que vous ayez trouvé la réponse!



0
votes

Lorsque vous supprimez le slotTime, y a-t-il une autre table qui a une clé étrangère liée à la table OpeningTimes? Il semble que vous supprimiez l'élément et que la table associée tente de définir la clé étrangère sur null, ce qui n'est pas autorisé. Vous devez vous assurer que les tables relatives aux heures d'ouverture sont modifiées de manière appropriée pour tenir compte de la suppression de l'enregistrement.

Edit: D'autres réponses sont plus détaillées et elles sont bien meilleures que les miennes.


0 commentaires