I'm running efcore 2.0.1.
I have a model:
public class BigAwesomeDinosaurWithTeeth
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public ICollection<YummyPunyPrey> YummyPunyPrey { get; set; }
}
public class YummyPunyPrey
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
public Guid? BigAwesomeDinosaurWithTeethId { get; set; }
[ForeignKey("BigAwesomeDinosaurWithTeethId")]
public BigAwesomeDinosaurWithTeeth BigAwesomeDinosaurWithTeeth { get; set; }
}
I have no fluent api on these two classes. But when I generate a migration
constraints: table =>
{
table.PrimaryKey("PK_YummyPunyPrey", x => x.Id);
table.ForeignKey(
name: "FK_YummyPunyPrey_BigAwesomeDinosaurWithTeeth_BigAwesomeDinosaurWithTeethId",
column: x => x.BigAwesomeDinosaurWithTeethId,
principalTable: "BigAwesomeDinosaurWithTeeth",
principalColumn: "Id",
onDelete: ReferentialAction.Restrict);
});
Why is it generating onDelete: ReferentialAction.Restrict when the documentation says it should handle it as a ClientSetNull
https://learn.microsoft.com/en-us/ef/core/saving/cascade-delete
Behavior Name | Effect on dependent/child in memory | Effect on dependent/child in database
ClientSetNull (Default) | Foreign key properties are set to null | None
Changes in EF Core 2.0: In previous releases, Restrict would cause optional foreign key properties in tracked dependent entities to be set to null, and was the default delete behavior for optional relationships. In EF Core 2.0, the ClientSetNull was introduced to represent that behavior and became the default for optional relationships. The behavior of Restrict was adjusted to never have any side effects on dependent entities.
Any help as to why this is happening would be much appreciated.
EF Core 2.0.1 metadata and migrations use different enums for specifying the delete behavior - respectively DeleteBehavior and ReferentialAction. While the first is well documented, the second and the mapping between the two is not (at the time of writing).
Here is the current mapping:
DeleteBehavior ReferentialAction
============== =================
Cascade Cascade
ClientSetNull Restrict
Restrict Restrict
SetNull SetNull
In your case, the relationship is optional, hence the DeleteBehavior by convention is ClientSetNull which maps to onDelete: Restrict, or in other words, enforced (enabled) FK w/o cascade delete.
If you want different behavior, you have to use fluent API, e.g.
modelBuilder.Entity<BigAwesomeDinosaurWithTeeth>()
.HasMany(e => e.YummyPunyPrey)
.WithOne(e => e.BigAwesomeDinosaurWithTeeth)
.OnDelete(DeleteBehavior.SetNull); // or whatever you like
Related
I am getting the following error when trying to add a migration:
PS C:\Code\morpher.ru\Morpher.Database> dotnet ef migrations add AddQazaqFeatures --startup-project=../Morpher.Database.Design
Build started...
Build succeeded.
System.InvalidOperationException: The association between entity types 'Service' and 'Deployment' has been severed but the relationship is either m
arked as 'Required' or is implicitly required because the foreign key is not nullable. If the dependent/child entity should be deleted when a requi
red relationship is severed, then setup the relationship to use cascade deletes. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLoggin
g' to see the key values.
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.HandleConceptualNulls(Boolean sensitiveLoggingEnabled, Boolean forc
e, Boolean isCascadeDelete)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.CascadeDelete(InternalEntityEntry entry, Boolean force, IEnumerable`1 fore
ignKeys)
...
My code:
public class Deployment
{
public int Id { get; set; }
public virtual Service Service { get; set; }
public int ServiceId { get; set; }
public string Host { get; set; }
public short? Port { get; set; }
public string BasePath { get; set; }
}
public class Service
{
public int Id { get; set; }
public string Name { get; set; }
public string UrlSlug { get; set; }
public virtual ICollection<Endpoint> Endpoints { get; set; }
public virtual ICollection<Deployment> Deployments { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Service>().HasData(new Service
{
Name = "Веб-сервис «Морфер»",
UrlSlug = "ws",
Id = 1
});
modelBuilder.Entity<Deployment>().HasData(new Deployment
{
Host = "ws3.morpher.ru",
ServiceId = 1,
Id = 1
});
modelBuilder.Entity<Deployment>(entity =>
{
entity.Property(e => e.Host).IsRequired().HasMaxLength(256);
entity.Property(e => e.BasePath).HasMaxLength(512);
entity.HasOne(deployment => deployment.Service)
.WithMany(service => service.Deployments)
.HasForeignKey(d => d.ServiceId)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("FK_Deployments_Services");
});
}
There are numerous StackOverflow questions mentioning the same error (1, 2, 3), but they are mostly to do with removing entities while not having a CASCADE delete policy or a nullable foreign key.
In my case, I am trying to add new rows and I don't see why it is considering the relationship 'severed'. Is setting ServiceId = 1 not enough?
I was able to reproduce the issue in latest at this time EF Core 3.1 version (3.1.28) by first removing the model data seeding code (HasData calls), then adding migration for just creating the tables/relationships, then adding the data seeding code and attempting to add new migration.
It does not happen in latest EF Core 6.0, so apparently you are hitting EF Core 3.1 defect/bug which has been fixed somewhere down on the road. So you either need to upgrade to a newer EF Core version (with all associated burdens like retesting everything, breaking changes etc.), or use the workaround below.
The workaround is to replace the DeleteBehavior.Restrict with either ClientNoAction or NoAction. Values of that enum and documentation of what they do is kind of messy, but all these 3 values seem to generate one and the same regular enforced FK constraint (with no cascade) in the database, and differ only by client side behavior, or in other words, what does EF Core change tracker do with related tracked entities when "deleting" a principal entity. And in this particular case, `Restrict" throws exception when there are tracked (loaded) related entity instances, while the other two won't.
I know you think you are just "adding data", but EF Core model data seeding is more than that - it tries to keep that data, so in some circumstances it needs to update or delete previously added data. Which in general works, except when there are bugs in the EF Core codebase, like in this case.
I have an Address class, that I use in a Customer class and in an Order class:
public class Address
{
public Customer Customer { get; set }
...
}
public class Customer
{
...
public List<Address> Addresses { get; set;}
}
public class Order
{
...
public Customer Curstomer { get; set; }
public Address BillingAddress { get; set;}
public Address ShippingAddress { get; set;}
}
I created the migrations succesfully but when I try to update-database I get the following error:
Introducing FOREIGN KEY constraint
'FK_Order_Address_ShippingAddressId' on table 'Order' 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.
What is the correct way of setting up such relationship? Is this modelling even correct? Because it seems strange to me that Address is a property related to Customer, but I'm also using it on Order, but duplicating the addresses in an OrderAddresses table seems wrong too.
To fix this, change the ReferentialAction for the onDelete of your foreign key in the migration to something other than Cascade. Restrict is probably a good option. It will look something like this:
constraints: table =>
{
table.PrimaryKey("PK_Order", x => x.Id);
table.ForeignKey(
name: "FK_Orders_BillingAddress_BillingAddressId",
column: x => x.BillingAddressId,
principalTable: "Addresses",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
}
Just change that to Restrict or another choice.
Why?
If you have Cascade, suppose you were to delete the BillingAddress of your Order. That would try to cascade the delete to the Order which would then cascade its delete to the ShippingAddress which would then try to cascade that delete back to the Order and so on and so forth, hence why SQL Server correctly errors out on cyclical cascading deletes.
See also this question: Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?
Table EMPLOYEE has MST_SQ (master-sequence) as both it's primary key, and as an FK to the primary key of table MASTER, which is also named MST_SQ. This table is used to join several other tables as well so that they all have the same PK. That is as far as my understanding goes.
I need to defined a 1 to 1 relationship in my model between class Employee and class Master, but I simply cannot find a way to do this. It seems only relationships with multiplicty allow an FK field to be speficied, and those that look like for 1 to 1, e.g. has optional(...)..WithRequiredPrincipal(....) has no FK space.
I could do some manual coding to link EMPLOYEE and MASTER when the are loaded, but how could I tell they were loaded. Is there any event that signals a POCO being populated from the DB? Or, the real question, how do I define this relationship in code?
From Relationships and Navigation Properties :
When working with 1-to-1 or 1-to-0..1 relationships, there is no
separate foreign key column, the primary key property acts as the
foreign key
From Configuring a Required-to-Optional Relationship (One-to–Zero-or-One) :
because the name of the property does not follow the convention the
HasKey method is used to configure the primary key
public class Master
{
public int MST_SQ { get; set; }
public virtual Employee Employee { get; set; }
}
public class Employee
{
public int MST_SQ { get; set; }
public virtual Master Master { get; set; }
}
The Employee has the MST_SQ property that is a primary key and a foreign key:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Master>().HasKey(m => m.MST_SQ);
modelBuilder.Entity<Employee>().HasKey(e => e.MST_SQ);
modelBuilder.Entity<Employee>()
.HasRequired(e => e.Master) //Employee is the Dependent and gets the FK
.WithOptional(m => m.Employee); //Master is the Principal
}
Generated migration code:
CreateTable(
"dbo.Employees",
c => new
{
MST_SQ = c.Int(nullable: false),
})
.PrimaryKey(t => t.MST_SQ)
.ForeignKey("dbo.Masters", t => t.MST_SQ)
.Index(t => t.MST_SQ);
CreateTable(
"dbo.Masters",
c => new
{
MST_SQ = c.Int(nullable: false, identity: true),
})
.PrimaryKey(t => t.MST_SQ);
So you don't need the "FK space" because EF makes it the foreign key without you having to specify it
I am a newbie in the EF. I read http://msdn.microsoft.com/en-us/data/gg193958.aspx and still confused.
I have an existing database and I'm writing a Code First model. I have Operators table with op_code Char(6) Primary Key. In the Operator class I named it OperatorCode, e.g.
[Key]
[Column("op_code",TypeName = "char")]
[DisplayName("Operator")]
public virtual string OperatorCode { get; set; }
In several of my tables I have EnteredBy and in some ModifiedBy columns that are FK to the op_code.
Say, for the Clients table I have both of these fields.
So, I added to the Operator class at the bottom:
[InverseProperty("EnteredBy")]
public virtual ICollection<Client> ClientsEnteredBy { get; set; }
[InverseProperty("ModifiedBy")]
public virtual ICollection<Client> ClientsUpdatedBy { get; set; }
and I added the following into the Client class:
public virtual Operator EnteredBy { get; set; }
public virtual Operator ModifiedBy { get; set; }
and I am getting a run-time error about EnteredBy_OperatorCode and ModifiedBy_OperatorCode columns.
What should I fix /add to let EF know my column names?
Thanks in advance.
Your foreign column names in the database do not match the default convention for FK names which is NavigationPropertyName_PrimaryKeyNameinTargetClass. Because your navigation properties are called EnteredBy and ModifiedBy and the primary key property is called OperatorCode EF expects - according to the mentioned convention - EnteredBy_OperatorCode and ModifiedBy_OperatorCode as foreign key columns. But those do not exist in the database which is the reason for your exception. Instead your FK columns are EnteredBy and ModifiedBy.
So, to fix the problem you must override the convention.
If you don't have FK properties in your model use Fluent API:
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsEnteredBy)
.WithRequired(c => c.EnteredBy) // or HasOptional
.Map(m => m.MapKey("EnteredBy")); // mapping for the FK column name
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsUpdatedBy)
.WithRequired(c => c.ModifiedBy) // or HasOptional
.Map(m => m.MapKey("ModifiedBy")); // mapping for the FK column name
(With this mapping you can remove the InverseProperty attribute.)
An alternative approach is to expose the FKs as properties in the model. Rename the navigation properties and use their names for the FK properties. The mapping is then possible with data annotations.
In Client class:
[ForeignKey("EnteredByOperator")]
public string EnteredBy { get; set; }
[InverseProperty("ClientsEnteredBy")]
public virtual Operator EnteredByOperator { get; set; }
[ForeignKey("ModifiedByOperator")]
public string ModifiedBy { get; set; }
[InverseProperty("ClientsUpdatedBy")]
public virtual Operator ModifiedByOperator { get; set; }
And remove the InverseProperty attributes in the Operator class.
Instead of the data annotations you can also use Fluent API:
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsEnteredBy)
.WithRequired(c => c.EnteredByOperator) // or HasOptional
.HasForeignKey(c => c.EnteredBy);
modelBuilder.Entity<Operator>()
.HasMany(o => o.ClientsUpdatedBy)
.WithRequired(c => c.ModifiedByOperator) // or HasOptional
.HasForeignKey(c => c.ModifiedBy);
If both relationships are required you will need to disable cascading delete for at least one of the relationships (append .WillCascadeOnDelete(false) at the end of one of the mappings), otherwise SQL Server will throw an error that multiple cascading delete paths between the tables are not allowed.
I would suggest to use the "alternative approach" (expose foreign keys as properties) because it is easier to work with in most cases.
When using Entity Framework Code First 4.3.1 it is possible to create relationships with a multiplicity of 1-to-1. That is, one entity on each end of the relationship.
It is possible to configure 1-to-1 relationships to be required-required or required-optional ^. However, when I switch between the two I do not see any differences in:
The database schema generated. I am targeting SQL Server 2008.
The runtime behaviour of EF.
As such, I am able to create a RequiredPrincipalAs record without a corresponding RequiredDependentAs record, despite the relationship being configured as required-required. This seems to contradict the documentation for HasRequired(...):
Configures a required relationship from this entity type. Instances of the entity type will not be able to be saved to the database unless this relationship is specified. The foreign key in the database will be non-nullable.
http://msdn.microsoft.com/en-us/library/gg671317
The required-required relationship entities:
public class RequiredPrincipalA
{
public int Id { get; set; }
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
The required-optional relationship entities:
public class RequiredPrincipalB
{
public int Id { get; set; }
public virtual OptionalDependentB DependentB { get; set; }
}
public class OptionalDependentB
{
public int Id { get; set; }
public virtual RequiredPrincipalB PrincipalB { get; set; }
}
The DbContext and model configuration:
public class AppContext : DbContext
{
public DbSet<RequiredPrincipalA> PrincipalAs { get; set; }
public DbSet<RequiredDependentA> DependentAs { get; set; }
public DbSet<RequiredPrincipalB> PrincipalBs { get; set; }
public DbSet<OptionalDependentB> DependentBs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<RequiredPrincipalA>()
.HasRequired(o => o.DependentA)
.WithRequiredPrincipal(o => o.PrincipalA);
modelBuilder.Entity<RequiredPrincipalB>()
.HasOptional(o => o.DependentB)
.WithRequired(o => o.PrincipalB);
}
}
The test code:
Database.SetInitializer(new DropCreateDatabaseAlways<AppContext>());
using (var ctx = new AppContext())
{
ctx.Database.Initialize(force: false);
ctx.PrincipalAs.Add(new RequiredPrincipalA());
ctx.PrincipalBs.Add(new RequiredPrincipalB());
ctx.SaveChanges();
}
I am aware I could add a [Required] data attribute to the navigation properties of RequiredPrincipalA.DependentA and RequiredDependentA.PrincipalA. This would cause EF validation to prevent the scenario above. However, I do not want to do this because it also validates the navigation property is populated when updating an existing entity. This means the application has to pre-fetch the entity at the other end of the relationship for every update.
Why do I not see any difference in the behaviour of EF just when changing a relationship between required-required and required-optional?
^ Note that optional-optional is also supported but this doesn't form part of my question. There are obvious differences in the generated database schema and runtime behaviour when an optional-optional relationship is configured.
I don't know why required-required is allowed for this case but it cannot exist in the database because relation is build on primary keys. Required-required means that A cannot be inserted if related B doesn't exist and B cannot be inserted if related A doesn't exist => neither A or B can be inserted.
Database relation has always principal and dependent entity - principal can always exist without dependent.
Real required-required in EF can be achieved only when both A and B are mapped to the same table (table splitting) because in such case they are both inserted with single insert command.
Not really an answer but I have more to say than will fit in comments. But you know, I write 900 page books...it's just how I roll. :)
Oddly I would expect the fluent configuration to behave the same way as the data annotation and am confused that it's not doing it. (I've pinged Rowan Miller with a link to this thread to get his feedback.) And the behavior I mean is: validating the constraint during SaveChanges.
On the database side, I'm with Ladislav.In the model, EF defines the 1:1 using the keys of the related entities. But in the database, you can't have FKs in both tables, so only the dependent table in the database will require that constraint that it's PK maps to an existing PK in the principal table.
And finally, I understand your reason for not wanting EF to enforce the relationship if you aren't going to always deal with teh full graph. I think 1:1 relationships are the most confusing of the EF relationship mappings and I always find myself having to go back for reminders of the rules and how things should work.
Old question. But since EF6 is still used and even available for .Net standard and this issue can be a real nuisance, I think it's worth mentioning something I couldn't find in other answers.
It is true that both HasRequired - WithRequiredPrincipal and HasOptional - WithRequired produce the same database schema and the same runtime behavior. That is, with both mappings it's possible to save a principal without a dependent entity and to remove the dependent later. So much for HasRequired.
But there is a way to make EF validate the required relationship when creating the entities, which is by simply adding a [Required] attribute:
public class RequiredPrincipalA
{
public int Id { get; set; }
[Required] // <== here
public virtual RequiredDependentA DependentA { get; set; }
}
public class RequiredDependentA
{
public int Id { get; set; }
public virtual RequiredPrincipalA PrincipalA { get; set; }
}
As said, only when creating the entities. It's still possible to set RequiredPrincipalA.RequiredDependentA = null and save it successfully. But I think that, fortunately, the likelihood of that happening in code is far lower than forgetting to set the required dependent.