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).
3 Réponses :
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:
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
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.
Cela "résoudrait" votre problème, mais devrait être évité si possible.
Dans le tableau cible, vous pouvez créer un indicateur supprimé et le définir sur true au lieu de la suppression réelle.
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 ...
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; } } } } }
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 codeDELETE >, 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.