Il vincolo di chiave esterna può causare cicli o più percorsi a cascata?

Ho un problema quando tento di aggiungere vincoli alle mie tabelle. Ottengo l’errore:

L’introduzione del vincolo FOREIGN KEY “FK74988DB24B3C886” sulla tabella “Dipendente” può causare cicli o più percorsi a cascata. Specificare ON DELETE NO ACTION o ON UPDATE NO ACTION o modificare altri vincoli FOREIGN KEY.

Il mio vincolo è tra una tabella di Code e una tabella di employee . La tabella Code contiene Id , Name , FriendlyName , Type e Value . Il employee ha un numero di campi che fanno riferimento a codici, in modo che possa esistere un riferimento per ciascun tipo di codice.

Ho bisogno che i campi siano nulli se il codice a cui si fa riferimento viene cancellato.

Qualche idea su come posso farlo?

    SQL Server esegue il conteggio semplice dei percorsi a cascata e, anziché tentare di stabilire se esistono effettivamente dei cicli, assume il peggio e rifiuta di creare le azioni referenziali (CASCADE): è ansible e dovrebbe comunque creare i vincoli senza le azioni referenziali. Se non puoi modificare il tuo progetto (o farlo comprometterebbe le cose), dovresti considerare l’uso dei trigger come ultima risorsa.

    La risoluzione dei percorsi a cascata FWIW è un problema complesso. Altri prodotti SQL ignoreranno semplicemente il problema e ti permetteranno di creare cicli, nel qual caso sarà una gara vedere quale sovrascriverà il valore per ultimo, probabilmente per l’ignoranza del progettista (ad esempio ACE / Jet lo fa). Capisco che alcuni prodotti SQL tenteranno di risolvere casi semplici. Resta il fatto, SQL Server non ci prova nemmeno, lo riproduce ultra sicuro disabilitando più di un percorso e almeno lo dice.

    Una situazione tipica con più percorsi in cascata sarà questa: una tabella principale con due dettagli, diciamo “Master” e “Detail1” e “Detail2”. Entrambi i dettagli sono eliminati in cascata. Finora nessun problema. Ma cosa succede se entrambi i dettagli hanno una relazione uno-a-molti con qualche altra tabella (ad esempio “SomeOtherTable”). SomeOtherTable ha una colonna Detail1ID AND una colonna Detail2ID.

     Master { ID, masterfields } Detail1 { ID, MasterID, detail1fields } Detail2 { ID, MasterID, detail2fields } SomeOtherTable {ID, Detail1ID, Detail2ID, someothertablefields } 

    In altre parole: alcuni dei record in SomeOtherTable sono collegati con i record Detail1 e alcuni dei record in SomeOtherTable sono collegati con i record Detail2. Anche se è garantito che i record di SomeOtherTable non appartengono mai a entrambi i dettagli, ora è imansible eliminare a cascata i record di SomeOhterTable per entrambi i dettagli, poiché ci sono più percorsi a cascata da Master a SomeOtherTable (uno tramite Detail1 e uno tramite Detail2). Ora potresti aver già capito questo. Ecco una ansible soluzione:

     Master { ID, masterfields } DetailMain { ID, MasterID } Detail1 { DetailMainID, detail1fields } Detail2 { DetailMainID, detail2fields } SomeOtherTable {ID, DetailMainID, someothertablefields } 

    Tutti i campi ID sono campi chiave e incremento automatico. Il punto cruciale si trova nei campi DetailMainId delle tabelle Detail. Questi campi sono sia contraffazioni chiave che referenziali. Ora è ansible eliminare a cascata tutto eliminando solo i record master. Lo svantaggio è che per ogni record detail1 e per ogni record detail2, deve esserci anche un record DetailMain (che in realtà viene creato prima per ottenere l’ID corretto e univoco).

    Vorrei sottolineare che (funzionalmente) c’è una GRANDE differenza tra i cicli e / o più percorsi nello SCHEMA e nei DATI. Mentre i cicli e forse i multipath nei DATI potrebbero certamente complicare l’elaborazione e causare problemi di prestazioni (costo di una gestione “corretta”), il costo di queste caratteristiche nello schema dovrebbe essere vicino allo zero.

    Poiché la maggior parte dei cicli apparenti in RDB si verificano nelle strutture gerarchiche (organigramma, parte, sottoparte, ecc.) È spiacevole che SQL Server assuma il peggio; cioè ciclo dello schema == ciclo di dati. Infatti, se stai usando i vincoli di RI non puoi effettivamente build un ciclo nei dati!

    Sospetto che il problema del multipath sia simile; cioè, più percorsi nello schema non implicano necessariamente più percorsi nei dati, ma ho meno esperienza con il problema di multipath.

    Ovviamente se SQL Server consentiva cicli, sarebbe comunque sobject a una profondità di 32, ma probabilmente è sufficiente per la maggior parte dei casi. (Peccato che non sia comunque un’impostazione di database!)

    I trigger “Anziché Elimina” non funzionano neanche. La seconda volta che viene visitata una tabella, il trigger viene ignorato. Quindi, se vuoi veramente simulare una cascata, dovrai usare le stored procedure in presenza di cicli. Tuttavia, il trigger di eliminazione dell’errore potrebbe funzionare per i casi di multipath.

    Celko suggerisce un modo “migliore” per rappresentare le gerarchie che non introducono cicli, ma ci sono dei compromessi.

    È disponibile un articolo in cui viene illustrato come eseguire più percorsi di eliminazione utilizzando i trigger. Forse questo è utile per scenari complessi.

    http://www.mssqltips.com/sqlservertip/2733/solving-the-sql-server-multiple-cascade-path-issue-with-a-trigger/

    Con il suono di ciò si ha un’azione OnDelete / OnUpdate su una delle chiavi esterne esistenti, che modificherà la tabella dei codici.

    Quindi, creando questa chiave esterna, si creerebbe un problema ciclico,

    Ad esempio, l’aggiornamento dei dipendenti, causa la modifica dei codici mediante un’azione di aggiornamento, provoca la modifica dei dipendenti mediante un’azione di aggiornamento in corso … ecc …

    Se pubblichi le tue definizioni di tabella per entrambe le tabelle, e le tue definizioni di chiave esterna / vincolo dovremmo essere in grado di dirti dove si trova il problema …

    Ciò è dovuto al fatto che Emplyee potrebbe avere una raccolta di altre quadro che afferma che le qualifiche e le qualifiche potrebbero avere altre università di raccolta, ad es

     public class Employee{ public virtual ICollection Qualifications {get;set;} 

    }

     public class Qualification{ public Employee Employee {get;set;} public virtual ICollection Universities {get;set;} 

    }

     public class University{ public Qualification Qualification {get;set;} 

    }

    Su DataContext potrebbe essere come sotto

     protected override void OnModelCreating(DbModelBuilder modelBuilder){ modelBuilder.Entity().HasRequired(x=> x.Employee).WithMany(e => e.Qualifications); modelBuilder.Entity.HasRequired(x => x.Qualification).WithMany(e => e.Universities); 

    }

    in questo caso c’è una catena dal Dipendente alla Qualifica e dalla Qualifica alle Università. Quindi mi stava facendo la stessa eccezione.

    Ha funzionato per me quando sono cambiato

      modelBuilder.Entity().**HasRequired**(x=> x.Employee).WithMany(e => e.Qualifications); 

    A

      modelBuilder.Entity().**HasOptional**(x=> x.Employee).WithMany(e => e.Qualifications); 

    Questo è un errore del tipo di politiche di trigger del database. Un trigger è codice e può aggiungere alcune intelligenze o condizioni a una relazione Cascade come Cascade Deletion. Potresti dover specializzare le opzioni relative alle tabelle in questo modo, ad esempio Distriggersre CascadeOnDelete :

     protected override void OnModelCreating( DbModelBuilder modelBuilder ) { modelBuilder.Entity().HasMany(i => i.Member).WithRequired().WillCascadeOnDelete(false); } 

    Oppure Distriggers completamente questa funzione:

     modelBuilder.Conventions.Remove(); 

    Trigger è la soluzione per questo problema:

     IF OBJECT_ID('dbo.fktest2', 'U') IS NOT NULL drop table fktest2 IF OBJECT_ID('dbo.fktest1', 'U') IS NOT NULL drop table fktest1 IF EXISTS (SELECT name FROM sysobjects WHERE name = 'fkTest1Trigger' AND type = 'TR') DROP TRIGGER dbo.fkTest1Trigger go create table fktest1 (id int primary key, anQId int identity) go create table fktest2 (id1 int, id2 int, anQId int identity, FOREIGN KEY (id1) REFERENCES fktest1 (id) ON DELETE CASCADE ON UPDATE CASCADE/*, FOREIGN KEY (id2) REFERENCES fktest1 (id) this causes compile error so we have to use triggers ON DELETE CASCADE ON UPDATE CASCADE*/ ) go CREATE TRIGGER fkTest1Trigger ON fkTest1 AFTER INSERT, UPDATE, DELETE AS if @@ROWCOUNT = 0 return set nocount on -- This code is replacement for foreign key cascade (auto update of field in destination table when its referenced primary key in source table changes. -- Compiler complains only when you use multiple cascased. It throws this compile error: -- Rrigger Introducing FOREIGN KEY constraint on table may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, -- or modify other FOREIGN KEY constraints. IF ((UPDATE (id) and exists(select 1 from fktest1 A join deleted B on B.anqid = A.anqid where B.id <> A.id))) begin update fktest2 set id2 = i.id from deleted d join fktest2 on d.id = fktest2.id2 join inserted i on i.anqid = d.anqid end if exists (select 1 from deleted) DELETE one FROM fktest2 one LEFT JOIN fktest1 two ON two.id = one.id2 where two.id is null -- drop all from dest table which are not in source table GO insert into fktest1 (id) values (1) insert into fktest1 (id) values (2) insert into fktest1 (id) values (3) insert into fktest2 (id1, id2) values (1,1) insert into fktest2 (id1, id2) values (2,2) insert into fktest2 (id1, id2) values (1,3) select * from fktest1 select * from fktest2 update fktest1 set id=11 where id=1 update fktest1 set id=22 where id=2 update fktest1 set id=33 where id=3 delete from fktest1 where id > 22 select * from fktest1 select * from fktest2 

    La mia soluzione a questo problema riscontrato usando ASP.NET Core 2.0 e EF Core 2.0 era di eseguire quanto segue in ordine:

    1. Eseguire il comando update-database in Package Management Console (PMC) per creare il database (il risultato è “Introdurre il vincolo FOREIGN KEY … potrebbe causare cicli o più percorsi a cascata.” Errore)

    2. Eseguire il comando script-migration -Idempotent in PMC per creare uno script che può essere eseguito indipendentemente dalle tabelle / vincoli esistenti

    3. Prendere lo script risultante e trovare ON DELETE CASCADE e sostituire con ON DELETE NO ACTION

    4. Esegui l’SQL modificato rispetto al database

    Ora le migrazioni dovrebbero essere aggiornate e le eliminazioni a catena non dovrebbero verificarsi.

    Peccato che non sia stato in grado di trovare alcun modo per farlo in Entity Framework Core 2.0.

    In bocca al lupo!