EF fluent API c# cascade delete issue - c#

I am having problems configuring a model to not cascade delete.
The modeL.
public class Hit
{
public int Id { get; set; }
// Foreign Key
public int AccountId { get; set; }
// Foreign Key
public int LeadId { get; set; }
//navigation properties
[ForeignKey("LeadId")]
public Lead lead { get; set; }
//navigation properties
[ForeignKey("AccountId")]
public Account account { get; set; }
}
Fluent API:
modelBuilder.Entity<Hit>()
.HasRequired(t => t.lead)
.WithMany()
.HasForeignKey(t => t.LeadId)
.WillCascadeOnDelete(false);
When I try to migrate it, i get this in create table on Hit:
.ForeignKey("dbo.Accounts", t => t.AccountId, cascadeDelete: true)
.ForeignKey("dbo.Leads", t => t.Lead_Id)
.ForeignKey("dbo.Leads", t => t.LeadId, cascadeDelete: true)
What I want to achieve for both leads and accounts is this in the migration script:
.ForeignKey("dbo.Leads", t => t.LeadId)
How should I go about doing this? Its prbably easy, but I am new to all this:(
EDIT 1:
I added this to the fluent API:
modelBuilder.Entity<Lead>().HasMany(i => i.Hits).WithOptional().HasForeignKey(s=> s.LeadId).WillCascadeOnDelete(false);
Which remove cascadelete from: .ForeignKey("dbo.Leads", t => t.LeadId)
However, I also want to remove it from:
.ForeignKey("dbo.Accounts", t => t.AccountId, cascadeDelete: true)
The easy solution would be to remove it from the migration script. But I would like how to do this with FLuent API or other methods.
Any ideas?

Like I already said in the comments above, if you use Fluent API for mapping your FKs remove the [ForeignKey] attributes from your navigation properties.
You can then do non-cascading mappings like this:
modelBuilder.Entity<Hit>().HasOptional(m => m.account).WithMany().HasForeignKey(m
=> m.AccountId).WillCascadeOnDelete(false);
Finally you have to check if your FK columns should be optional or required, for optional mappings change your FK properties AccountId and LeadId to data type int?, otherwise you have to change the HasOptional() part of your FK mappings to HasRequired().

Define your model like so:
public class Hit
{
public int Id { get; set; }
public virtual Lead Lead { get; set; }
public virtual Account Account { get; set; }
}
The virtual keyword will let Entity Framework know you are defining a foreign key and will match on Id automatically.
Remove the HasRequired(t => t.lead).WithMany() part from Fluent API. If a property is required it cannot exist without it and will cascade on delete.

Related

How to design multiple one-to-many relationship between 2 tables with entity framework core [duplicate]

I've been wrestling with this for a while and can't quite figure out what's happening. I have a Card entity which contains Sides (usually 2) - and both Cards and Sides have a Stage. I'm using EF Codefirst migrations and the migrations are failing with this error:
Introducing FOREIGN KEY constraint 'FK_dbo.Sides_dbo.Cards_CardId' on
table 'Sides' may cause cycles or multiple cascade paths. Specify ON
DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
Here's my Card entity:
public class Card
{
public Card()
{
Sides = new Collection<Side>();
Stage = Stage.ONE;
}
[Key]
[Required]
public virtual int CardId { get; set; }
[Required]
public virtual Stage Stage { get; set; }
[Required]
[ForeignKey("CardId")]
public virtual ICollection<Side> Sides { get; set; }
}
Here's my Side entity:
public class Side
{
public Side()
{
Stage = Stage.ONE;
}
[Key]
[Required]
public virtual int SideId { get; set; }
[Required]
public virtual Stage Stage { get; set; }
[Required]
public int CardId { get; set; }
[ForeignKey("CardId")]
public virtual Card Card { get; set; }
}
And here's my Stage entity:
public class Stage
{
// Zero
public static readonly Stage ONE = new Stage(new TimeSpan(0, 0, 0), "ONE");
// Ten seconds
public static readonly Stage TWO = new Stage(new TimeSpan(0, 0, 10), "TWO");
public static IEnumerable<Stage> Values
{
get
{
yield return ONE;
yield return TWO;
}
}
public int StageId { get; set; }
private readonly TimeSpan span;
public string Title { get; set; }
Stage(TimeSpan span, string title)
{
this.span = span;
this.Title = title;
}
public TimeSpan Span { get { return span; } }
}
What's odd is that if I add the following to my Stage class:
public int? SideId { get; set; }
[ForeignKey("SideId")]
public virtual Side Side { get; set; }
The migration runs successfully. If I open up SSMS and look at the tables, I can see that Stage_StageId has been added to Cards (as expected/desired), however Sides contains no reference to Stage (not expected).
If I then add
[Required]
[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int StageId { get; set; }
To my Side class, I see StageId column added to my Side table.
This is working, but now throughout my application, any reference to Stage contains a SideId, which is in some cases totally irrelevant. I'd like to just give my Card and Side entities a Stage property based on the above Stage class without polluting the stage class with reference properties if possible... what am I doing wrong?
Because Stage is required, all one-to-many relationships where Stage is involved will have cascading delete enabled by default. It means, if you delete a Stage entity
the delete will cascade directly to Side
the delete will cascade directly to Card and because Card and Side have a required one-to-many relationship with cascading delete enabled by default again it will then cascade from Card to Side
So, you have two cascading delete paths from Stage to Side - which causes the exception.
You must either make the Stage optional in at least one of the entities (i.e. remove the [Required] attribute from the Stage properties) or disable cascading delete with Fluent API (not possible with data annotations):
modelBuilder.Entity<Card>()
.HasRequired(c => c.Stage)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Side>()
.HasRequired(s => s.Stage)
.WithMany()
.WillCascadeOnDelete(false);
I had a table that had a circular relationship with others and I was getting the same error. Turns out it is about the foreign key which was not nullable. If the key is not nullable the related object must be deleted, and circular relations don't allow that. So use nullable foreign key.
[ForeignKey("StageId")]
public virtual Stage Stage { get; set; }
public int? StageId { get; set; }
Anybody wondering how to do it in EF core:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
..... rest of the code.....
I was getting this error for lots of entities when I was migrating down from an EF7 model to an EF6 version. I didn't want to have to go through each entity one at a time, so I used:
builder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
builder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
You can set cascadeDelete to false or true (in your migration Up() method). Depends upon your requirement.
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
In .NET Core I changed the onDelete option to ReferencialAction.NoAction
constraints: table =>
{
table.PrimaryKey("PK_Schedule", x => x.Id);
table.ForeignKey(
name: "FK_Schedule_Teams_HomeId",
column: x => x.HomeId,
principalTable: "Teams",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
table.ForeignKey(
name: "FK_Schedule_Teams_VisitorId",
column: x => x.VisitorId,
principalTable: "Teams",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
});
I had this issue also, I solved it instantly with this answer from a similar thread
In my case, I didn't want to delete the dependent record on key deletion. If this is the case in your situation just simply change the Boolean value in the migration to false:
AddForeignKey("dbo.Stories", "StatusId", "dbo.Status", "StatusID", cascadeDelete: false);
Chances are, if you are creating relationships which throw this compiler error but DO want to maintain cascade delete; you have an issue with your relationships.
I fixed this. When you add the migration, in the Up() method there will be a line like this:
.ForeignKey("dbo.Members", t => t.MemberId, cascadeDelete:True)
If you just delete the cascadeDelete from the end it will work.
Just for documentation purpose, to someone that comes on the future, this thing can be solved as simple as this, and with this method, you could do a method that disabled one time, and you could access your method normally
Add this method to the context database class:
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
}
In .NET Core I played with all upper answers - but without any success.
I made changes a lot in DB structure and every time added new migration attempting to update-database, but received the same error.
Then I started to remove-migration one by one until Package Manager Console threw me exception:
The migration '20170827183131_***' has already been applied to the database
After that, I added new migration (add-migration) and update-database successfully
So my suggestion would be: clear out all your temp migrations, until your current DB state.
public partial class recommended_books : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.RecommendedBook",
c => new
{
RecommendedBookID = c.Int(nullable: false, identity: true),
CourseID = c.Int(nullable: false),
DepartmentID = c.Int(nullable: false),
Title = c.String(),
Author = c.String(),
PublicationDate = c.DateTime(nullable: false),
})
.PrimaryKey(t => t.RecommendedBookID)
.ForeignKey("dbo.Course", t => t.CourseID, cascadeDelete: false) // was true on migration
.ForeignKey("dbo.Department", t => t.DepartmentID, cascadeDelete: false) // was true on migration
.Index(t => t.CourseID)
.Index(t => t.DepartmentID);
}
public override void Down()
{
DropForeignKey("dbo.RecommendedBook", "DepartmentID", "dbo.Department");
DropForeignKey("dbo.RecommendedBook", "CourseID", "dbo.Course");
DropIndex("dbo.RecommendedBook", new[] { "DepartmentID" });
DropIndex("dbo.RecommendedBook", new[] { "CourseID" });
DropTable("dbo.RecommendedBook");
}
}
When your migration fails you are given a couple of options:
'Introducing FOREIGN KEY constraint 'FK_dbo.RecommendedBook_dbo.Department_DepartmentID' on table 'RecommendedBook' 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.'
Here is an example of using the 'modify other FOREIGN KEY constraints' by setting 'cascadeDelete' to false in the migration file and then run 'update-database'.
Make your Foreign key attributes nullable. That will work.
This sounds weird and I don't know why, but in my case that was happening because my ConnectionString was using "." in "data source" attribute. Once I changed it to "localhost" it workded like a charm. No other change was needed.
The existing answers are great I just wanted to add that I ran into this error because of a different reason. I wanted to create an Initial EF migration on an existing DB but I didn't use the -IgnoreChanges flag and applied the Update-Database command on an empty Database (also on the existing fails).
Instead I had to run this command when the current db structure is the current one:
Add-Migration Initial -IgnoreChanges
There is likely a real problem in the db structure but save the world one step at a time...
In .NET 5 < and .NET Core 2.0 < you can use .OnDelete(DeleteBehavior.Restrict) in OnModelCreating like #Nexus23 answer but you do not need to disable cascade for every model.
Example with join entity type configuration many-to-many:
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId)
.OnDelete(DeleteBehavior.Restrict),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId)
.OnDelete(DeleteBehavior.Restrict),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
Sources:
https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key#join-entity-type-configuration
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-5.0
This does require you to remove the many to many relationship yourself or you will receive the following error when you remove a parent entity:
The association between entity types '' and '' has been severed, but
the relationship is either marked as required or is implicitly
required because the foreign key is not nullable. If the
dependent/child entity should be deleted when a required relationship
is severed, configure the relationship to use cascade deletes.
Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to
see the key values
You can solve this by using DeleteBehavior.ClientCascade instead which will allow EF to perform cascade deletes on loaded entities.
internal class MyContext : DbContext
{
public MyContext(DbContextOptions<MyContext> options)
: base(options)
{
}
public DbSet<Post> Posts { get; set; }
public DbSet<Tag> Tags { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Post>()
.HasMany(p => p.Tags)
.WithMany(p => p.Posts)
.UsingEntity<PostTag>(
j => j
.HasOne(pt => pt.Tag)
.WithMany(t => t.PostTags)
.HasForeignKey(pt => pt.TagId)
.OnDelete(DeleteBehavior.Cascade),
j => j
.HasOne(pt => pt.Post)
.WithMany(p => p.PostTags)
.HasForeignKey(pt => pt.PostId)
.OnDelete(DeleteBehavior.ClientCascade),
j =>
{
j.Property(pt => pt.PublicationDate).HasDefaultValueSql("CURRENT_TIMESTAMP");
j.HasKey(t => new { t.PostId, t.TagId });
});
}
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public ICollection<Tag> Tags { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class Tag
{
public string TagId { get; set; }
public ICollection<Post> Posts { get; set; }
public List<PostTag> PostTags { get; set; }
}
public class PostTag
{
public DateTime PublicationDate { get; set; }
public int PostId { get; set; }
public Post Post { get; set; }
public string TagId { get; set; }
public Tag Tag { get; set; }
}
https://learn.microsoft.com/en-us/dotnet/api/microsoft.entityframeworkcore.deletebehavior?view=efcore-5.0
None of the aforementioned solutions worked for me. What I had to do was use a nullable int (int?) on the foreign key that was not required (or not a not null column key) and then delete some of my migrations.
Start by deleting the migrations, then try the nullable int.
Problem was both a modification and model design. No code change was necessary.
The simple way is to, Edit your migration file (cascadeDelete: true) into (cascadeDelete: false) then after assign the Update-Database command in your Package Manager Console.if it's problem with your last migration then all right. Otherwise check your earlier migration history, copy those things, paste into your last migration file, after that do it the same thing. it perfectly works for me.
You could add this in your DataContext.cs, this works for me...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
}
I ran into the same problem and stuck for a long. The following steps saved me.
Go through the constraints and change the onDelete ReferentialAction to NoAction from Cascade
constraints: table =>
{
table.PrimaryKey("PK_table1", x => x.Id);
table.ForeignKey(
name: "FK_table1_table2_table2Id",
column: x => x.table2Id,
principalTable: "table2",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction);
});

Entity Framework : Invalid column name *_ID even with fluent api or data annotations

Odd issue that I've been looking at all day. I am working with Entity Framework 6. The issue I have is that I have three entities:
public partial class Order : ILocationBearingObject
{
public int Id { get; set; }
// other properties and relationships here
public int? OrderProfileId { get; set; }
public int OrderTemplateId { get; set; }
public virtual OrderProfile Profile { get; set; } // optional property
public virtual OrderTemplate OrderTemplate{ get; set; }
}
public class OrderProfile
{
public int Id { get; set; }
// other properties
// added here 6/15/2021
public virtual OrderTemplate OrderTemplate{ get; set; }
}
public class OrderTemplate : EntityMetaData
{
public int Id { get; set; }
// other properties
public int? OrderProfileId{ get; set; }
public OrderProfile OrderProfile { get; set; }
}
In our model builder, we have these definitions:
modelBuilder.Entity<Order>()
.HasOptional(x => x.OrderProfile)
.WithMany(x => x.Orders)
.HasForeignKey(x => x.OrderProfileId);
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile);
But even with the above fluent api model, we get the error
Invalid column name 'OrderProfile_Id'
Throughout various testing I was unable to find why this issue was occurring, so I looked at our logs and found when this error started popping it's head up and then was able to find the changes associated to OrderProfile and found that the only change that was made was adding the relationship from OrderProfile to OrderTemplate.
When I removed that fluent api relationship OrderProfile to OrderTemplate, it worked as expected... I don't need that relationship to OrderTemplate, but would like it to be there, how can I establish a optional 1 to optional 1 relationship without breaking other relationships? Also, why would additional relationships be effected by this?
UPDATE 6/15/2021
So I found I had a reverse navigation property in the OrderProfile model:
public virtual OrderTemplate OrderTemplate{ get; set; }
removing that and the associated fluent relationship
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile);
Doing the above resolved the issue, but for some reason, the issue seems to have cascaded down to another relationship that has a circular reference like the above. The Order class is involved with this cascaded issue. I guess this is a pretty big cause for concern since this application worked fine for the last 4 years and for these relationships to be decaying like this is worrisome. Does anyone know why this is happening?
if you use the right naming convention, EF will do magic. in this sample, you don't need fluent API to relate entities.
public partial class Order : ILocationBearingObject
{
public int Id { get; set; }
public int? OrderProfileId { get; set; } //means HasOptional (nullable) and ForeignKey
//variable name must be OrderProfile not Profile
public virtual OrderProfile OrderProfile { get; set; }
}
public class OrderProfile
{
public OrderProfile()
{
Orders = new HashSet<Order>();
}
public int Id { get; set; }
//be aware circular reference at any conversion or mapping
public virtual ICollection<Order> Orders {get; set;} //means WithMany
}
I've got an error like this too. It's caused by unmatching OrderProfileId property in OrderTemplate class with the fluent api model
If I'm not wrong, you want the OrderProfile model a many to many relation between Order and OrderTemplate. Then if it was the case, add the nvaigation property in OrderProfile.
public class OrderProfile
{
public int Id { get; set; }
// other properties
public virtual ICollection<Order> Orders { get; set; }
public virtual OrderTemplate OrderTemplate { get; set; }
}
Then change the fluent api model to be like this
// the EF has modelled the relation for normal 1 to many relation
// modelBuilder.Entity<Order>()
// .HasOptional(x => x.OrderProfile)
// .WithMany(x => x.Orders)
// .HasForeignKey(x => x.OrderProfileId);
modelBuilder.Entity<OrderTemplate>()
.HasOptional(x => x.OrderProfile)
.WithOptional(x => x.OrderTemplate);
You're working database-first, which always leaves room for a mismatch between the actual database model and the model EF infers from class and property names and mapping code (= conceptual model). If this happens, it may help to make EF generate a database from the conceptual model and see where it creates the column it expects, OrderProfile_Id.
This is what you'll see when logging the SQL statements:
CREATE TABLE [dbo].[OrderTemplates] (
[Id] [int] NOT NULL IDENTITY,
[OrderProfileId] [int],
[OrderProfile_Id] [int],
CONSTRAINT [PK_dbo.OrderTemplates] PRIMARY KEY ([Id])
)
...
ALTER TABLE [dbo].[OrderTemplates]
ADD CONSTRAINT [FK_dbo.OrderTemplates_dbo.OrderProfiles_OrderProfile_Id]
FOREIGN KEY ([OrderProfile_Id]) REFERENCES [dbo].[OrderProfiles] ([Id])
There you see the expected nullable column OrderProfile_Id which is the FK to OrderProfiles. It's noteworthy to see that EF does not use OrderProfileId as a foreign key field. It's just a field that could be used for anything.
That's because EF6 doesn't support 1:1 associations as foreign key associations (reference property and primitive FK property).
Knowing this, the remedy is simple: remove the property OrderTemplate.OrderProfileId and tell EF to use the field OrderTemplate.OrderProfileId in the database:
modelBuilder.Entity<OrderProfile>()
.HasOptional(x => x.OrderTemplate)
.WithOptionalPrincipal(x => x.OrderProfile)
.Map(m => m.MapKey("OrderProfileId"));
That said, I wonder why Order has a foreign key to OrderProfile. Isn't its OrderProfile determined by its OrderTemplate? If it's a redundant relationship it may be better to remove it.

Configuring one-to-many foreign key to a non primary key property

This maybe an easy question but I didn't manage to figure it out after hours of reading Microsoft documentation. I have following 2 models in my DB (I simplified them for better understanding):
public class Schedule
{
public int Id { get; set; }
...
public string Role { get; set; }
}
public class Roles
{
public int Id { get; set; }
...
public string RoleDisplay { get; set; }
}
I need to set up Role string from Schedule model as a foreign key with one-to-many relation to RoleDisplay key in Roles model. So in Schedule we can have a lot of records with Role cell pointing to the Roles table.
If it was regular Id key I would add
public Roles Roles { get; set; }
to Schedule and EF would automatically generate corresponding RoleID column in the Schedule table.
The issue is that I'm not dealing with primary key and the key naming convention doesn't match standard
<TableName><ColumnName>
I heard that this can be set up in Fluent API, something like:
modelBuilder.Entity<Schedule>()
.HasOne()
.WithMany()
.HasForeignKey();
but I didn't figure out what needs to be put in brackets for each statement.
Can you please help me?
Since you don't have any navigation property on either entity, you have to use the generic version HasOne<Roles>() to indicate which table the foreign-key refers to. Once that is clear to EF, you don't need to pass anything to WithMany(). Try the following -
Declare RoleDisplay as an alternate-key on Roles -
modelBuilder.Entity<Roles>(e =>
{
e.HasKey(p => p.Id);
e.HasAlternateKey(p => p.RoleDisplay);
});
While configuring the foreign-key on Schedule, specify which key on Roles it should target -
modelBuilder.Entity<Schedule>(e =>
{
e.HasKey(p => p.Id);
});
modelBuilder.Entity<Schedule>()
.HasOne<Roles>()
.WithMany()
.HasForeignKey(p => p.Role)
.HasPrincipalKey(p => p.RoleDisplay);
Hope that helps.
This will work. It is pretty easy. I still don't understand what is it you don't didn't understand?
public class Schedule
{
public int Id { get; set; }
public Role Role { get; set; }
}
public class Role
{
public int Id { get; set; }
public string RoleDisplay { get; set; }
public ICollection<Schedule> Schedules { get; set; }
}

ef core multiple 1 to 1 or zero same class

trying to implement two instances of a 1 to 1 or 1 to zero navigation property in ef core using either data annotations or fluent api.
formation table is just id and name.
public class Formation {
public long Id { get; set; }
public string Name {get; set;}
}
the well class can have but does not need to have a deepestformation of type formation
the well class can have but does not need to have a producingformation of type formation
public class Well
{
public long Id { get; set; }
public long? DeepestFormationId { get; set; }
public Formation DeepestFormation { get; set; }
public long? ProducingFormationId { get; set; }
public Formation ProducingFormation { get; set; }
}
I am struggling how to set this up using either data annotations or the fluid api
I attempted
modelBuilder.Entity<Well>()
.HasOne(c => c.DeepestFormation)
.WithOne()
.HasForeignKey<Formation>(c => c.Id);
but where do I tell it that the foreign key is DeepestFormationId?
Firstly, I'd recommend you read ef core Definition of terms.
Principal key: The properties that uniquely identify the principal entity. This may be the primary key or an alternate key.
Foreign key: The properties in the dependent entity that are used to store the principal key values for the related entity.
Then you can try to change your mapping:
modelBuilder.Entity<Well>()
.HasOne(c => c.DeepestFormation)
.WithOne()
.HasForeignKey<Well>(c => c.DeepestFormationId);
modelBuilder.Entity<Well>()
.HasOne(c => c.ProducingFormation)
.WithOne()
.HasForeignKey<Well>(c => c.ProducingFormationId);
You optionally can specify the principle key - .HasPrincipalKey(item => item.Id), but Ef core smart enough to resolve it.
Then you need to consider OnDelete behavior. Be careful with it, if you set Cascade, in your case you might get Multiple cascade path exception

EF CascadeOnDelete Code First

I have this entity:
public class Account
{
[Key]
[ForeignKey("Company")]
[Required]
public Guid CompanyId { get; set; }
public virtual Company Company { get; set; }
}
And this one:
public class Company : PrimaryKey
{
public string Name { get; set; }
public virtual ICollection<Contact> Contacts { get; set; }
public virtual Account Account { get; set; }
}
How do I use fluent api to enable cascade delete, I tried this:
modelBuilder.Entity<Company>().HasOptional<Account>().WithRequired().WillCascadeOnDelete();
But I have no idea what this means. Basically, I want a Company to have an optional Account which will be deleted when the company is deleted.
The mapping you need is:
modelBuilder.Entity<Company>()
.HasOptional(c => c.Account)
.WithRequired(a => a.Company)
.WillCascadeOnDelete();
It's a one-to-one relationship between Company and Account. With this mapping you can remove the [ForeignKey("Company")] attribute and the [Required] attribute anyway because a Guid is not nullable and therefore always required.
Please folow the link Enabling Cascade Delete.
You can configure cascade delete on a relationship by using the
WillCascadeOnDelete method. If a foreign key on the dependent entity
is not nullable, then Code First sets cascade delete on the
relationship. If a foreign key on the dependent entity is nullable,
Code First does not set cascade delete on the relationship, and when
the principal is deleted the foreign key will be set to null.
You can remove these cascade delete conventions by using:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>()
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>()
The following code configures the relationship to be required and then
disables cascade delete.
modelBuilder.Entity<Course>()
.HasRequired(t => t.Department)
.WithMany(t => t.Courses)
.HasForeignKey(d => d.DepartmentID)
.WillCascadeOnDelete(false);
something like it in your example:
modelBuilder
.Entity<Company>()
.HasOptional<Account>()
.HasForeignKey(a => a.CompanyId)
.WillCascadeOnDelete();

Categories

Resources