-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simplification of cascade delete fluent API #8654
Comments
@roji, @ErikEJ and everyone, we were wondering if you could provide any insights on the most appropriate default cascade behavior for nullable FKs across databases you are familiar with. There are two "schools of thought" on this:
|
The SQL Compact engine only support CASCADE and NO ACTION https://technet.microsoft.com/en-us/library/ms173393(v=sql.110).aspx |
Add Default enum and make it the default. It will be the hybrid behavior--set null in the state manager, restrict in the database. |
Is this just the behavior on SQL Server, or the semantic meaning of In other words, if a provider supports |
I think that's an open question, but probably yes. However, it might be better for that provider to have a convention that makes SetNull the default. |
A few more thoughts on this
|
@divega How about this? /// <summary>
/// <para>
/// Indicates how a delete operation is applied to dependent entities in a relationship when the
/// principal is deleted or the relationship is severed.
/// </para>
/// <para>
/// Behaviors in the database are dependent on the database schema being created
/// appropriately. Using Entity Framework Migrations or <see cref="DatabaseFacade.EnsureCreated" />
/// will create the appropriate schema.
/// </para>
/// <para>
/// Note that the in-memory behavior for entities that are currently tracked by
/// the <see cref="DbContext" /> can be different from the behavior that happens in the database.
/// See the <see cref="Hybrid" /> behavior for more details.
/// </para>
/// </summary>
public enum DeleteBehavior
{
/// <summary>
/// <para>
/// For entities being tracked by the <see cref="DbContext" />, the values of foreign key properties in
/// dependent entities are set to null. This helps keep the graph of entities in a consistent
/// state while they are being tracked, such that a fully consistent graph can then be written to
/// the database. If a property cannot be set to null because it is not a nullable type,
/// then an exception will be thrown when <see cref="DbContext.SaveChanges()" /> is called.
/// This is the same as the <see cref="SetNull" /> behavior.
/// </para>
/// <para>
/// If the database has been created from the model using Entity Framework Migrations or the
/// <see cref="DatabaseFacade.EnsureCreated" /> method, then the behavior in the database is a
/// sensible default for that database provider. Some databases cannot easily support the
/// <see cref="SetNull" /> type behavior described above for tracked entities, especially if there are
/// cycles in relationships. Therefore, for many database providers, the default behavior in the
/// database will be equivalent to a <see cref="Restrict" /> behavior where an error will be generated if a
/// foreign key constraint is violated.
/// </para>
/// <para>
/// This is the default for optional relationships. That is, for relationships that have
/// nullable foreign keys.
/// </para>
/// </summary>
Hybrid,
/// <summary>
/// <para>
/// For entities being tracked by the <see cref="DbContext" />, the values of foreign key properties in
/// dependent entities are not changed. This can result in an inconsistent graph of entities
/// where the values of foreign key properties do not match the relationships in the
/// graph. If a property remains in this state when <see cref="DbContext.SaveChanges()" />
/// is called, then an exception will be thrown.
/// </para>
/// <para>
/// If the database has been created from the model using Entity Framework Migrations or the
/// <see cref="DatabaseFacade.EnsureCreated" /> method, then the behavior in the database
/// is to generate an error if a foreign key constraint is violated.
/// </para>
/// </summary>
Restrict,
/// <summary>
/// <para>
/// For entities being tracked by the <see cref="DbContext" />, the values of foreign key properties in
/// dependent entities are set to null. This helps keep the graph of entities in a consistent
/// state while they are being tracked, such that a fully consistent graph can then be written to
/// the database. If a property cannot be set to null because it is not a nullable type,
/// then an exception will be thrown when <see cref="DbContext.SaveChanges()" /> is called.
/// </para>
/// <para>
/// If the database has been created from the model using Entity Framework Migrations or the
/// <see cref="DatabaseFacade.EnsureCreated" /> method, then the behavior in the database is
/// the same as is described above for tracked entities. Keep in mind that some databases cannot easily
/// support this behavior, especially if there are cycles in relationships.
/// </para>
/// </summary>
SetNull,
/// <summary>
/// <para>
/// For entities being tracked by the <see cref="DbContext" />, the dependent entities
/// will also be deleted when <see cref="DbContext.SaveChanges()" /> is called.
/// </para>
/// <para>
/// If the database has been created from the model using Entity Framework Migrations or the
/// <see cref="DatabaseFacade.EnsureCreated" /> method, then the behavior in the database is
/// the same as is described above for tracked entities. Keep in mind that some databases cannot easily
/// support this behavior, especially if there are cycles in relationships.
/// </para>
/// <para>
/// This is the default for required relationships. That is, for relationships that have
/// non-nullable foreign keys.
/// </para>
/// </summary>
Cascade
} |
@ajcvickers when I made my previous comment I missed that A few suggestions for your proposal above:
|
I don't agree with this statement. The .NET idiom of an enum value named |
@bricelam That was only the secondary argument. The primary argument is that there is actually no default value for this enum:
|
…and Restrict in the database Issues #8654, #8632, #8633 The new behavior is called 'ClientSetNull'. 'Restrict' now throws on SaveChanges if EF would have set null. This is the same behavior that has always been there for required relationships, so this is only a break for optional relationships explicitly configured with Restrict, which should be relatively rare since this was the default anyway. The default is now 'ClientSetNull', which matches the old behavior of 'Restrict', so apps that were not setting anything explicitly will not get a breaking change.
…and Restrict in the database Issues #8654, #8632, #8633 The new behavior is called 'ClientSetNull'. 'Restrict' now throws on SaveChanges if EF would have set null. This is the same behavior that has always been there for required relationships, so this is only a break for optional relationships explicitly configured with Restrict, which should be relatively rare since this was the default anyway. The default is now 'ClientSetNull', which matches the old behavior of 'Restrict', so apps that were not setting anything explicitly will not get a breaking change.
…and Restrict in the database Issues #8654, #8632, #8633 The new behavior is called 'ClientSetNull'. 'Restrict' now throws on SaveChanges if EF would have set null. This is the same behavior that has always been there for required relationships, so this is only a break for optional relationships explicitly configured with Restrict, which should be relatively rare since this was the default anyway. The default is now 'ClientSetNull', which matches the old behavior of 'Restrict', so apps that were not setting anything explicitly will not get a breaking change.
…and Restrict in the database Issues #8654, #8632, #8633 The new behavior is called 'ClientSetNull'. 'Restrict' now throws on SaveChanges if EF would have set null. This is the same behavior that has always been there for required relationships, so this is only a break for optional relationships explicitly configured with Restrict, which should be relatively rare since this was the default anyway. The default is now 'ClientSetNull', which matches the old behavior of 'Restrict', so apps that were not setting anything explicitly will not get a breaking change.
Note: we ended up not implementing the proposal below but what is described in #8654 (comment) and subsequent comments
While looking at customer reported issues related to
OnDelete(DeleteBehavior)
fluent API, we came to the conclusion that what we have today in EF Core is just too confusing:DeleteBehavior
conflates change tracking and database behavior in a single place and the behaviors resulting from the options available (Cascade
,SetNull
andRestrict
) do not align with users' expectations.There are reasons for the misalignment:
SetNull
the default for nullable FKs andRestrict
the default for non-nullable FKs. The change tracking and database behavior could align!SetNull
orCascade
results frequently in cycles, which typically yield this error:The backup plan was to:
Restrict
the default behavior in the databaseRestrict
orSetNull
was specified.DeleteBehavior.Cascade
makes the change tracking and database behaviors alignProposal:
Obsolete the
OnDelete
fluent API and theDeleteBehavior
enum used in its argumentCreate a new simple
bool
-based API that indicates only if deletes should cascade. This will control both the change tracking and (indirectly) the database behavior. Several naming alternatives are:WillCascadeOnDelete
: this is what we had in EF6CascadeOnDelete
: shortened version of the aboveOnDeleteCascade
: closer name to current API in EF Core and closer to the SQL syntaxEF Core will continue to have current default behaviors, e.g. it will set it to
false
if the FK is nullable,true
if the FK is non-nullable. When the API is called with no arguments, that meanstrue
. Otherwise we use whatever Boolean value is specified.Relational can have its own API that represent more granularly what can go in the database's
ON DELETE
clause. We need to look at how easy it is to drop down to metadata to specify annotations in the model for this. Incidentally, we already have this enum that seems to cover all the necessary options in migrations:When cascade deletes are disabled, the behavior in change tracking will continue to be setting nullable keys to null and tracking conceptual nulls for non-nullable keys.
Providers will be free to choose how to best match the default change tracking behavior. E.g. in the case of SQL Server,
OnDeleteCascade(false)
will result inON DELETE NO ACTION
, but on a different provider for a database which doesn't fail eagerly on cycles, it could produceON DELETE SET NULL
.The text was updated successfully, but these errors were encountered: