Foreign key Entity Framework - code first migrations - c#

I have a problem with my entities. I'm using EF code-first migrations and the migrations are failing with this error:
Introducing FOREIGN KEY constraint 'FK_OrdersChildsProducts_Orders_OrderId' on table 'OrdersChildsProducts' 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 PersonJceProfile entity :
[Table("PersonJceProfiles")]
public class PersonJceProfile : BaseEntity
{
[ForeignKey("Ces")]
public int? CeId { get; set; }
public ICollection<Child> Children { get; set; }
public Order Order { get; set; }
public PersonJceProfile()
{
Children = new List<Child>();
}
}
Here's my Order entity :
[Table("Orders")]
public class Order : BaseEntity
{
[Key]
public int Id { get; set; }
//ForeignKey
[Required]
[ForeignKey("PersonJceProfiles")]
public int PersonJceProfileId { get; set; }
[Required]
public int OrderStatus { get; set; }
[Required]
public bool IsSecurePayment { get; set; }
public int LeftToPayPersonOrder { get; set; }
public string Delivery { get; set; }
public ICollection<OrderChildProduct> OrderChildProduct { get; set; }
public Order()
{
OrderChildProduct = new Collection<OrderChildProduct>();
}
}
Here's my Child entity :
[Table("Childrens")]
public class Child :BaseEntity
{
[Key]
public int Id { get; set; }
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
[Required]
public DateTime? BirthDate { get; set; }
[Required]
public string Gender { get; set; }
public bool? IsActif { get; set; }
public decimal AmountParticipationCe { get; set; }
public bool? IsRegrouper { get; set; }
[Required]
[ForeignKey("PersonJceProfiles")]
public int PersonJceProfileId { get; set; }
}
Here's my Product Entity
public class Product : Good
{
public string File { get; set; }
public bool? IsDisplayedOnJCE { get; set; }
public bool? IsBasicProduct { get; set; }
public int? PintelSheetId { get; set; }
public int OriginId { get; set; }
[Required]
[ForeignKey("Suppliers")]
public int SupplierId { get; set; }
}
Here's my OrderChildProduct entity :
[Table("OrdersChildsProducts")]
public class OrderChildProduct
{
public int OrderId { get; set; }
public int ChildId { get; set; }
public int ProductId { get; set; }
public int LeftToPayChildOrder { get; set; }
public Order Order { get; set; }
public Child Child { get; set; }
public Product Product { get; set; }
}
Here's my context :
modelBuilder.Entity<OrderChildProduct>().HasKey(ccp => new { ccp.OrderId, ccp.ChildId, ccp.ProductId });
I suppose i do destro a relationship like this :
modelBuilder.Entity<Entity>()
.HasRequired(c => c.ForeignKey)
.WithMany()
.WillCascadeOnDelete(false);
but I can't see between which. Because
When I delete PersonJceProfiles : Order must be deleted - OrderChildProduct must be deleted - Child must be deleted
When I delete Order : OrderChildProduct must be deleted
When I delete Order childProduct : nothing must be deleted expect himself
What am I doing wrong? Thanks

The error is self described. You must configure the relationships in the OrderChildProduct table like:
entity.HasOne(p => p.Order)
.WithMany(p => p.OrderChildProduct)
.HasForeignKey(p => p.OrderId)
.OnDelete(Microsoft.EntityFrameworkCore.Metadata.DeleteBehavior.Restrict);
You can use either Restrict or Cascade depending on your requirements.
Also the other 2 relationships must be defined as well.
This is not an error. This is more like a warning... the way EF tells you that it doesn't fully understand the relationships and you must configure them manually.

Related

Reference the same entity by two different entities Ef Core

I am a bit confused about this, I am implementing a Wish List in which multiple users can add the same entity (Product) to their list.
I extended the identity user class and added an ICollection<Product>
but i cant seem to figure out how can multiple users reference the same product in terms of foreign keys, because whenever a user add a product to their wish list it gets deleted from the previous user as the foreign key now references the new user. obviously my logic is flawed and i misunderstood how the relationship should be defined can you please point me in the right direction?
here is the Product entity
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int ProductId { get; set; }
public string ProductName { get; set; }
public virtual ProductType ProductType { get; set; }
[ForeignKey("Category")]
public int SubcategoryFK { get; set; }
public virtual SubCategory Category { get; set; }
public decimal Price { get; set; }
public int NumberOfItems { get; set; }
public string ImageUrl { get; set; }
public string IsInStock { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
[ForeignKey("SalesUser")]
public string SalesUserFK { get; set; }
public virtual ApplicationUser SalesUser { get; set; }
and the relationship is configured like this
modelBuilder.Entity<ApplicationUser>()
.HasMany(a => a.Products)
.WithOne(p => p.SalesUser).HasForeignKey(z => z.SalesUserFK).OnDelete(DeleteBehavior.ClientSetNull);
As Kei commented , the relationship of Product and User is many-to-many. In EF Core ,Many-to-many relationships without an entity class to represent the join table are not yet supported. However, you can represent a many-to-many relationship by including an entity class for the join table and mapping two separate one-to-many relationships.
public class Product
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Key]
public int ProductId { get; set; }
public string ProductName { get; set; }
public virtual ProductType ProductType { get; set; }
[ForeignKey("Category")]
public int SubcategoryFK { get; set; }
public virtual SubCategory Category { get; set; }
public decimal Price { get; set; }
public int NumberOfItems { get; set; }
public string ImageUrl { get; set; }
public string IsInStock { get; set; }
public string ShortDescription { get; set; }
public string LongDescription { get; set; }
public List<UserProduct> UserProducts { get; set; }
}
public class ApplicationUser:IdentityUser
{
public List<UserProduct> UserProducts { get; set; }
}
public class UserProduct
{
public int ProductId { get; set; }
public Product Product { get; set; }
public Guid ApplicationUserId { get; set; }
public ApplicationUser ApplicationUser { get; set; }
}
DbContext :
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserProduct>()
.HasKey(up => new { up.ProductId, up.ApplicationUserId });
modelBuilder.Entity<UserProduct>()
.HasOne(up => up.Product)
.WithMany(p => p.UserProducts)
.HasForeignKey(up => up.ProductId);
modelBuilder.Entity<UserProduct>()
.HasOne(up => up.ApplicationUser)
.WithMany(au => au.UserProducts)
.HasForeignKey(up => up.ApplicationUserId);
}
Reference : Many-to-many relationship and Cascade Delete

Linq Include not loading List

I've been for a while trying to find out why the Include clause is not loading the related collection: I have two classes with a one-to-many relationship:
public class AgencyNote : IAutId
{
[Key]
public int aut_id { get; set; }
public string Comment { get; set; }
[DisplayName("Note Created Date")]
public DateTime NoteDate { get; set; }
[DisplayName("Contact Date")]
public DateTime ContactDate { get; set; }
[ForeignKey("tbl_agency")]
public int AgencyId { get; set; }
[DisplayName("User")]
public string RipsUser { get; set; }
public virtual ICollection<AgencyNoteAttachment> AgencyNoteAttachments { get; set; }
public virtual tbl_agency tbl_agency { get; set; }
}
and
public class AgencyNoteAttachment
{
[Key]
public int aut_id { get; set; }
public string Url { get; set; }
public string FileName { get; set; }
public int AgencyNoteId { get; set; }
[NotMapped]
[ForeignKey("AgencyNoteId")]
public virtual AgencyNote AgencyNote { get; set; }
}
Context class:
public DbSet<AgencyNote> AgencyNotes { get; set; }
public DbSet<AgencyNoteAttachment> AgencyNoteAttachments { get; set; }
This is the action where I'm using the Include clause:
private IQueryable<AgencyNote> GetNotes(int agencyId)
{
return _ctx.AgencyNotes
.Include(a => a.tbl_agency)
.Include(a => a.AgencyNoteAttachments)
.OrderByDescending(f => f.NoteDate)
.Where(x => x.AgencyId == agencyId);
}
I'm getting AgencyNotesAttachments always null from this action even if I know it's not null, what's going on? Any question let me know...
If you add just the navigation properties between the related entities, then EF will create the FK column for you in the AgencyNoteAttachment table. Now, EF by convention can interpret AgencyNoteId is the FK of that relationship, but is good idea do that explicitly as you already have in your model or using ForeignKey attribute on FK property:
public class AgencyNoteAttachment
{
[Key]
public int aut_id { get; set; }
public string Url { get; set; }
public string FileName { get; set; }
[ForeignKey("AgencyNote")]
public int AgencyNoteId { get; set; }
public virtual AgencyNote AgencyNote { get; set; }
}
If you want to learn more about conventions, take a look this link

Entity Framework multiple references to same table

I'm having trouble creating my database with EF code-first. I have an entity Player and an entity friedship.
Each friendship references two players. One of the players is the sender, the other one is the receiver of the friendship.
This are my entities:
Player.cs
public class Player
{
public int PlayerId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[InverseProperty("Receiver")]
public virtual List<Friendship> FriendshipsIncoming { get; set; }
[InverseProperty("Sender")]
public virtual List<Friendship> FriendshipsOutgoing { get; set; }
}
Friendship.cs
public class Friendship
{
public int FriendshipId { get; set; }
public int SenderId { get; set; }
public int ReceiverId { get; set; }
[ForeignKey("Sender")]
public Player Sender { get; set; }
[ForeignKey("Receiver")]
public Player Receiver { get; set; }
[Required]
public bool Confirmed { get; set; }
}
I tried implementing the relationsships the way shown in this tutorial:
http://www.entityframeworktutorial.net/code-first/inverseproperty-dataannotations-attribute-in-code-first.aspx
When trying to update the database with the "update-database" command i'm getting the following error message:
The ForeignKeyAttribute on property 'Receiver' on type 'Darta.WebApi.Models.Friendship' is not valid. The foreign key name 'Receiver' was not found on the dependent type 'Darta.WebApi.Models.Friendship'. The Name value should be a comma separated list of foreign key property names.
I also tried fixing the issue with fluent-api like shown here:
http://csharpwavenet.blogspot.sg/2013/06/multiple-foreign-keys-with-same-table.html
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Friendship>()
.HasRequired(b => b.Sender)
.WithMany(a => a.FriendshipsOutgoing)
.HasForeignKey(b=>b.SenderId);
modelBuilder.Entity<Friendship>()
.HasRequired(b => b.Receiver)
.WithMany(a => a.FriendshipsIncoming)
.HasForeignKey(b => b.ReceiverId);
}
In this case I'm getting the following error:
Introducing FOREIGN KEY constraint 'FK_dbo.Friendships_dbo.Players_SenderId' on table 'Friendships' 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.
You should only need either the DataAnnotations or the FluentAPI. You don't need both. If you want to use the [ForeignKey] and [InverseProperty] attributes, then get rid of the FluentAPI code.
Also, note that in the [ForeignKey] and [InverseProperty] attributes, you need to specify the name of the column, not the navigation property.
public class Player
{
public int PlayerId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[InverseProperty("ReceiverId")]
public virtual ICollection<Friendship> FriendshipsIncoming { get; set; }
[InverseProperty("SenderId")]
public virtual ICollection<Friendship> FriendshipsOutgoing { get; set; }
}
public class Friendship
{
public int FriendshipId { get; set; }
public int SenderId { get; set; }
public int ReceiverId { get; set; }
[ForeignKey("SenderId")]
public Player Sender { get; set; }
[ForeignKey("ReceiverId")]
public Player Receiver { get; set; }
[Required]
public bool Confirmed { get; set; }
}
I'll correct the answer. InverseProperty must be a valid entity type. So in this case Friendship.Receiver, Friendship.Sender
public class Player
{
public int PlayerId { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
[InverseProperty("Receiver")]
public virtual ICollection<Friendship> FriendshipsIncoming { get; set; }
[InverseProperty("Sender")]
public virtual ICollection<Friendship> FriendshipsOutgoing { get; set; }
}
public class Friendship
{
public int FriendshipId { get; set; }
public int SenderId { get; set; }
public int ReceiverId { get; set; }
[ForeignKey("SenderId")]
public Player Sender { get; set; }
[ForeignKey("ReceiverId")]
public Player Receiver { get; set; }
[Required]
public bool Confirmed { get; set; }
}

EF - Code First FOREIGN KEY constraint may cause cycles or multiple cascade paths

I have been up to trying to do some Entity Framework Code First, but I am stuck with the 'FOREIGN KEY constraint may cause cycles or multiple cascade paths.' problem.
Here are my classes:
public class Course
{
public Course()
{
this.Subject = new HashSet<Subject>();
this.Student = new HashSet<Student>();
}
public int Id { get; set; }
public string Name { get; set; }
public string Shift { get; set; }
public int Room { get; set; }
public virtual ICollection<Subject> Subject { get; set; }
public virtual ICollection<Student> Student { get; set; }
}
public class Subject
{
public Subject()
{
this.Deliverable = new HashSet<Deliverable>();
}
public int Id { get; set; }
public string Name { get; set; }
public int StartsAt { get; set; }
public int FinishesAt { get; set; }
public System.TimeSpan Weekdays { get; set; }
public int CourseId { get; set; }
public int TeacherId { get; set; }
public virtual Course Course { get; set; }
public virtual Teacher Teacher { get; set; }
public virtual ICollection<Deliverable> Deliverable { get; set; }
}
public class Student : Person
{
public Student()
{
this.Deliverable = new HashSet<Deliverable>();
}
public decimal Average { get; set; }
public int CourseId { get; set; }
public virtual Course Course { get; set; }
public virtual ICollection<Deliverable> Deliverable { get; set; }
}
public class Teacher : Person
{
public Teacher()
{
this.Subject = new HashSet<Subject>();
}
public virtual ICollection<Subject> Subject { get; set; }
}
public class Deliverable
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Mark { get; set; }
public System.DateTime DeliveredDate { get; set; }
public bool Delivered { get; set; }
public System.DateTime AnnounceDate { get; set; }
public int SubjectId { get; set; }
public int StudentId { get; set; }
public virtual Subject Subject { get; set; }
public virtual Student Student { get; set; }
}
I think it's a reference looping error, but I can't realize the approach on how to resolve it. I'm using Web API and I'm able to change the model, so feel free to modify it please. Could this be resolved using FluentAPI?
Here is the exception. It is thrown the first time I have executed the application:
'Introducing FOREIGN KEY constraint 'FK_dbo.Students_dbo.Courses_CourseId' on table 'Students' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.'
You have to use Fluent-API to disable delete / update. To do this, modify the OnModelCreating method:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Course>().HasMany(c => c.Student).WillCascadeOnDelete(false);
}
I'm not sure if it's the Course class or the Student class who causes issues, if this is not working, try to do a "WillCascadeOnDelete(false)" on your Student class.

Defining many to many relation in code first entity framework

I have 3 classes in my model as you can see below.
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserName { get; set; }
public ICollection<MartialArtUserProfile> MartialArtUserProfiles { get; set; }
}
[Table("MartialArt")]
public class MartialArt
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string IconPath { get; set; }
public string ImagePath { get; set; }
public ICollection<MartialArtUserProfile> MartialArtUserProfiles { get; set; }
}
public class MartialArtUserProfile
{
public int UserProfileId { get; set; }
public UserProfile UserProfile { get; set; }
public int MartialArtId { get; set; }
public MartialArt MartialArt { get; set; }
}
And I have a configuration class for many to many relationship as below:
public class MartialArtUserProfileConfiguration : EntityTypeConfiguration<MartialArtUserProfile>
{
public MartialArtUserProfileConfiguration()
{
HasKey(a => new { a.MartialArtId, a.UserProfileId });
HasRequired(a => a.MartialArt)
.WithMany(s => s.MartialArtUserProfiles)
.HasForeignKey(a => a.MartialArtId)
.WillCascadeOnDelete(false);
HasRequired(a => a.UserProfile)
.WithMany(p => p.MartialArtUserProfiles)
.HasForeignKey(a => a.UserProfileId)
.WillCascadeOnDelete(false);
}
}
After defining my entities an relation when I try to run Update-Database in Package Manager Console, it says:
One or more validation errors were detected during model generation:
\tSystem.Data.Entity.Edm.EdmEntityType: : EntityType 'MartialArtUserProfile' has no key defined. Define the key for this EntityType.
\tSystem.Data.Entity.Edm.EdmEntitySet: EntityType: EntitySet 'MartialArtUserProfiles' is based on type 'MartialArtUserProfile' that has no keys defined.
What am I doing wrong?
Thanks in advance,
If I understand you are simply trying to create a many to many with a transitive table. If so this is another way to approach this. Use Fluent API to map as below. You can change the UserProfileToMartialArt to whatever you want the table name to be. Instead of creating the MartialArtUserProfile model let EF create the middle ground for you. This also specifies your keys which should get you around the error.
modelBuilder.Entity<UserProfile>()
.HasMany(b => b.MartialArts)
.WithMany(a => a.UserProfiles)
.Map(m => m.MapLeftKey("MartialArtId")
.MapRightKey("UserProfileId")
.ToTable("UserProfileToMartialArt"));
In MartialArts Model put
public IList<UserProfile> UserProfiles { get; set; }
In UserProfile Model put
public IList<MartialArt> MartialArts { get; set; }
Try doing it like this:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string UserName { get; set; }
[InverseProperty("UserProfiles")]
public IList<MartialArt> MartialArts { get; set; }
}
[Table("MartialArt")]
public class MartialArt
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public string IconPath { get; set; }
public string ImagePath { get; set; }
[InverseProperty("MartialArts")]
public IList<UserProfile> UserProfiles { get; set; }
}
In EntityFramework 6.1, you don't need to do any of this - just add collections of the two types to each class and everything falls into place.
public class UserProfile {
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<MartialArt> MartialArts { get; set; }
public UserProfile() {
MartialArts = new List<MartialArt>();
}
}
public class MartialArt {
public int Id { get; set; }
public string Name { get; set; }
// *snip*
public virtual ICollection<UserProfile> UserProfiles { get; set; }
public MartialArt() {
UserProfiles = new List<UserProfile>();
}
}

Categories

Resources