Sto usando EF Core 1.1.0 e ho un problema serio con il comportamento a cascata.
Ho un modello chiamato Land come muggito:
public class Land
{
public long Id { get; set; }
public int HorizontalPosition { get; set; }
public int VerticalPosition { get; set; }
public bool IsPlaced { get; set; }
// Relations
public string UserId { get; set; }
public virtual User User { get; set; }
public long? BuildingId { get; set; }
public virtual Building Building { get; set; }
}
e un altro modello chiamato Building come questo:
public class Building
{
public long Id { get; set; }
public bool IsPermanent { get; set; }
public int UpgradeCount { get; set; }
// Relations
public string UserId { get; set; }
public virtual User User { get; set; }
public int BuildingTypeId { get; set; }
public virtual BuildingType BuildingType { get; set; }
public virtual List<Land> Lands { get; set; }
}
e in dbcontext ho menzionato una relazione come questa:
...
modelBuilder.Entity<Land>()
.HasOne(l => l.Building)
.WithMany(b => b.Lands)
.HasForeignKey(l => l.BuildingId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.SetNull);
...
ma quando provo ad aggiornare il database ottengo questo errore:
System.Data.SqlClient.SqlException: Introducing FOREIGN KEY constraint 'FK_Lands_Buildings_BuildingId' on table 'Lands' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlCommand.RunExecuteNonQueryTds(String methodName, Boolean async, Int32 timeout, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite, String methodName)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.Execute(IRelationalConnection connection, String executeMethod, IReadOnlyDictionary`2 parameterValues, Boolean closeConnection)
at Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.ExecuteNonQuery(IEnumerable`1 migrationCommands, IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_1.<.ctor>b__0()
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
ClientConnectionId:65b8e08e-2d17-46ac-b734-385f88dd07b1
Error Number:1785,State:0,Class:16
Introducing FOREIGN KEY constraint 'FK_Lands_Buildings_BuildingId' on table 'Lands' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
la migrazione generata è qualcosa del genere:
...
migrationBuilder.AddForeignKey(
name: "FK_Lands_Buildings_BuildingId",
table: "Lands",
column: "BuildingId",
principalTable: "Buildings",
principalColumn: "Id",
onDelete: ReferentialAction.SetNull);
...
Ho detto che imposta la chiave esterna su null su delete ma non vuole che io faccia nessuna azione. Sebbene in dbcontext non ci sia alcun comportamento chiamato 'NoAction'!
In EF Core versione 2.2.6 è presente la voce ClientSetNull
nell'enum Microsoft.EntityFrameworkCore.DeleteBehavior
.
L'uso di ClientSetNull
invece di SetNull
fatto la differenza nel mio caso.
Non capisco perché MsSqlServer non sia in grado di risolvere più percorsi a cascata, altri motori sono perfettamente in grado di farlo.