2
votes

ForeignKey SetNull OnSupprimer

Je voudrais savoir s'il est possible, avec une clé étrangère composite, de définir une seule colonne de cette clé étrangère sur null lorsque je supprime la ligne associée.

En effet, la deuxième colonne (celle que je ne veux pas être nulle) est utilisée pour d'autres clés étrangères que je ne souhaite pas désactiver.

Précision: j'utilise Entity Framework Core (dernière version).


1 commentaires

La seule façon que je pourrais penser de faire cela serait soit de faire en sorte que les instructions DELETE soient effectuées via un SP à la place, et la ou les lignes enfants sont mises à jour avant le code DELETE , ou vous utilisez un déclencheur INSTEAD OF . Si vous pouvez imposer l'utilisation des procédures stockées, je voudrais personnellement cette méthode.


3 Réponses :


2
votes

Pour autant que je sache, cela n'est pas possible, car votre valeur de clé étrangère ferait alors référence à un enregistrement inexistant. Cependant, il existe des solutions de contournement que vous pouvez utiliser:

Une autre colonne

Vous pouvez créer une autre colonne avec la même valeur et votre ancienne colonne ne référencera que la table où vous avez l'intention de faire la suppression, la clé étrangère vers les autres tables serait liée à la nouvelle colonne avec la même valeur

Ne pas définir de clé étrangère pour cette table

Vous pouvez éviter de définir une clé étrangère pour la table dans laquelle vous pourriez supprimer des enregistrements, de sorte qu'aucune contrainte de ce type ne vous affecterait.

Désactivation de la vérification de la clé étrangère

Cela "résoudrait" votre problème, mais devrait être évité si possible.

Suppression virtuelle

Dans le tableau cible, vous pouvez créer un indicateur supprimé et le définir sur true au lieu de la suppression réelle.


0 commentaires

0
votes

Merci pour votre réponse.

Je suis d'accord avec tout ce que vous avez dit sauf une chose:

Pour autant que je sache, cela n'est pas possible, car votre valeur de clé étrangère ferait alors référence à un enregistrement inexistant.

Le fait qu'une des colonnes soit nullable (celle que je veux être définie sur NULL), me permet de faire en sorte que le FK {ParentId: null, TenantId: 4} ne référence aucune ligne. Si je définis le ParentId, le FK fait maintenant référence à une ligne.

Dans mon cas, je veux juste que si je supprime le Parent, seul le ParentId soit défini sur null et non le TenantId qui est utilisé pour d'autres FK et aussi dans le PK. Mais je suis arrivé à la même conclusion que vous que ce n'est pas possible ...


0 commentaires

0
votes

Enfin, j'ai remplacé la méthode SaveChanges pour explorer les fils et définir toute la partie colonne du FK sur null (à l'exception du TenantId).

Voici le code permettant d'itérer sur une propriété de navigation d'entité:

foreach (var navigationEntry in entry.Navigations
                            .Where(n => !n.Metadata.IsDependentToPrincipal()))
{
    if (navigationEntry is CollectionEntry collectionEntry)
    {
        // FK uses DeleteBehavior.ClientSetNull -> let's set it to NULL
        if (((Microsoft.EntityFrameworkCore.Metadata.Internal.Navigation)((MemberEntry)navigationEntry).Metadata).Builder.Metadata.ForeignKey.DeleteBehavior == DeleteBehavior.ClientSetNull)
        {
            // getting all fields composing the FK (except TenantId)
            List<string> fieldsToSetToNull = ((Microsoft.EntityFrameworkCore.Metadata.Internal.Navigation)((MemberEntry)navigationEntry).Metadata).Builder.Metadata.ForeignKey.Properties.Where(x => x.Name != nameof(IMustHaveTenant.TenantId)).Select(x => x.Name).ToList();

            List<object> dependentEntitiesList = new List<object>((IEnumerable<object>)collectionEntry.CurrentValue);
            for (var i = dependentEntitiesList.Count - 1; i >= 0; i--)
            {
                // setting all fields to NULL
                foreach (string fi in fieldsToSetToNull)
                {
                    var childEntry = this.Entry(dependentEntitiesList[i]);
                    childEntry.CurrentValues[fi] = null;
                }
            }
        }
    }
}



0 commentaires