Utilizzo Entity Framework Core e ho una tabella:
public class BlogComment
{
public int Id { get; set; }
public BlogPost Post { get; set; }
[StringLength(100)]
public string AuthorName { get; set; }
[StringLength(254)]
public string AuthorEmail { get; set; }
public bool SendMailOnReply { get; set; }
[StringLength(2000)]
public string Content { get; set; }
public DateTime CreatedTime { get; set; }
public int? ReplyToId { get; set; }
public BlogComment ReplyTo { get; set; }
}
Da questo, EFC genera la seguente tabella:
CREATE TABLE [dbo].[BlogComment] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[AuthorEmail] NVARCHAR (254) NULL,
[AuthorName] NVARCHAR (100) NULL,
[Content] NVARCHAR (2000) NULL,
[CreatedTime] DATETIME2 (7) NOT NULL,
[PostId] INT NULL,
[ReplyToId] INT NULL,
[SendMailOnReply] BIT NOT NULL,
CONSTRAINT [PK_BlogComment] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_BlogComment_BlogPost_PostId] FOREIGN KEY ([PostId]) REFERENCES [dbo].[BlogPost] ([Id]),
CONSTRAINT [FK_BlogComment_BlogComment_ReplyToId] FOREIGN KEY ([ReplyToId]) REFERENCES [dbo].[BlogComment] ([Id])
);
GO
CREATE NONCLUSTERED INDEX [IX_BlogComment_PostId]
ON [dbo].[BlogComment]([PostId] ASC);
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_BlogComment_ReplyToId]
ON [dbo].[BlogComment]([ReplyToId] ASC) WHERE ([ReplyToId] IS NOT NULL);
Alcuni commenti vengono inviati come risposta a un altro, ma non a tutti. Quando il commento originale viene eliminato, la risposta diventa un commento normale. Quindi, seguendo questo tutorial , l'aspetto della configurazione è questo:
modelBuilder.Entity<BlogComment>()
.HasOne(p => p.ReplyTo)
.WithOne()
.HasForeignKey<BlogComment>(c => c.ReplyToId)
.IsRequired(false)
.OnDelete(DeleteBehavior.SetNull);
Il metodo di eliminazione è piuttosto semplice:
var comment = await context.BlogComment.Include(c => c.ReplyTo).SingleAsync(m => m.Id == id);
context.BlogComment.Remove(comment);
await context.SaveChangesAsync();
Ma non riesco a eseguirlo, ricevo un errore:
System.Data.SqlClient.SqlException: l'istruzione DELETE è in conflitto con il vincolo SAME TABLE REFERENCE "FK_BlogComment_BlogComment_ReplyToId".
Come posso risolvere questo?
Per concludere la conversazione nei commenti:
Innanzitutto, l'autoreferenzialità è un'associazione 1: n:
modelBuilder.Entity<BlogComment>()
.HasOne(p => p.ReplyTo)
.WithMany(c => c.Replies)
.HasForeignKey(c => c.ReplyToId)
.IsRequired(false)
.OnDelete(<we'll get to that>);
Quindi, solo per comodità, ora BlogComment
ha anche una proprietà
public ICollection<BlogComment> Replies { get; set; }
Tuttavia, non posso creare la tabella usando
.OnDelete(DeleteBehavior.SetNull);
Mi dà
L'introduzione del vincolo FOREIGN KEY "FK_BlogComments_BlogComments_ReplyToId" nella tabella "BlogComments" può causare cicli o più percorsi a cascata.
Questa è una limitazione del server Sql che dobbiamo solo accettare, non c'è modo di evitarlo. L'unico modo per ottenere il comportamento a cascata desiderato è
.OnDelete(DeleteBehavior.ClientSetNull);
Che è :
Per le entità monitorate da DbContext, i valori delle proprietà di chiavi esterne nelle entità dipendenti sono impostati su null. Ciò consente di mantenere il grafico delle entità in uno stato coerente mentre vengono monitorate, in modo tale che un grafico completamente coerente possa essere quindi scritto nel database. (...) Questa è l'impostazione predefinita per le relazioni opzionali.
Ad esempio: il client esegue SQL per annullare i valori delle chiavi esterne. Tuttavia, i record figlio dovrebbero essere tracciati. Per rimuovere un genitore di BlogComment
l'azione di eliminazione dovrebbe essere simile a:
using (var db = new MyContext(connectionString))
{
var c1 = db.BlogComments
.Include(c => c.Replies) // Children should be included
.SingleOrDefault(c => c.Id == 1);
db.BlogComments.Remove(c1);
db.SaveChanges();
}
Come vedi, non devi impostare ReplyToId = null
, è qualcosa a cui EF si prende cura.
Per me, ho dovuto Include()
le entità che dovevo essere "trattate" quando ho cancellato un'entità. EF non può gestire cose che non sta attualmente monitorando.
var breedToDelete = context.Breed
.Include(x => x.Cats)
.Single(x => x.Id == testBreedId);
context.Breed.Remove(breedToDelete);
context.SaveChanges();