I have aproject with codefirst migrations wiht the following classes:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Chat> Chats { get; set; }
}
and
public class Chat
{
[Key, Column(Order = 0)]
public int User1Id { get; set; }
public virtual User User1 { get; set; }
[Key, Column(Order = 1)]
public int User2Id { get; set; }
public virtual User User2 { get; set; }
public string Channel { get; set; }
}
The Idea is A chat has two users, and the chat Primary key is a composite key of the two user Ids, which are foreign keys.
A user has also a list of chats, in which he participates.
now when I try to run this, I get the following error:
Introducing FOREIGN KEY constraint 'FK_dbo.Chat_dbo.User_User2Id' on table 'Chat' 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. See previous errors.
I kind of understand what's happening, but I don't know how to fix it. Tried this (from a topic here somewhere):
public DbSet<User> Users { get; set; }
public DbSet<Chat> Chats { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Chat>()
.HasRequired(s => s.User1)
.WithMany()
.WillCascadeOnDelete(true);
modelBuilder.Entity<Chat>()
.HasRequired(s => s.User2)
.WithMany()
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
but with no result, i still get the error. Can anyone shine some light on this matter?
try
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Chat> InitiatedChats { get; set; }
public virtual ICollection<Chat> ParticipantInChats { get; set; }
}
public class Chat
{
[Key, Column(Order = 0)]
public int User1Id { get; set; }
public virtual User User1 { get; set; }
[Key, Column(Order = 1)]
public int User2Id { get; set; }
public virtual User User2 { get; set; }
public string Channel { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Chat>()
.HasRequired(s => s.User1)
.WithMany(u => u.InitiatedChats)
.HasForeignKey(s => s.User1Id)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Chat>()
.HasRequired(s => s.User2)
.WithMany(u => u.ParticipantInChats)
.HasForeignKey(s => s.User2Id)
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
The anwser comes from this post
Related
I have this simple model and I'd want to configure relations between them
one Company has one logo
and one Company can have many files
public class Company
{
public int Id { get; set; }
public File Logo { get; set; }
public List<File> Attachments { get; set; } = new List<File>();
}
public class File
{
public int Id { get; set; }
public Company Company { get; set; }
public int CompanyId { get; set; }
(...)
}
public class CompanyConfiguration : IEntityTypeConfiguration<Company>
{
public void Configure(EntityTypeBuilder<Company> builder)
{
builder
.HasMany(x => x.Attachments)
.WithOne(x => x.Company)
.HasForeignKey(x => x.CompanyId);
builder
.HasOne(x => x.Logo)
.WithOne(x => x.Company)
.HasForeignKey(x => x.); // x. doesnt show me anything about "File" class. It looks like assembly
}
}
But since I probably neeed two FK
I changed it into:
public class File
{
public int Id { get; set; }
public Company Company { get; set; }
public int CompanyId { get; set; }
public Company CompanyOtherProperty { get; set; }
public int CompanyOtherPropertyId { get; set; }
}
but even if I insert FK's name as a string
public class CompanyConfiguration : IEntityTypeConfiguration<Company>
{
public void Configure(EntityTypeBuilder<Company> builder)
{
builder
.HasMany(x => x.Attachments)
.WithOne(x => x.Company)
.HasForeignKey(x => x.CompanyId);
builder
.HasOne(x => x.Logo)
.WithOne(x => x.CompanyOtherProperty)
.HasForeignKey("CompanyOtherPropertyId");
}
}
System.InvalidOperationException: 'You are configuring a relationship between 'Company' and 'File' but have specified a foreign key on 'CompanyOtherPropertyId'. The foreign key must be defined on a type that is part of the relationship.'
Given your needs, I would do this using inheritance since Logo and Attachements have common properties but not the same relations
public abstract class File
{
public int Id { get; set; }
.....
}
public class Logo : File
{
....
}
public class Attachement : File
{
public Company Company { get; set; }
public int CompanyId { get; set; }
}
public class Company
{
public int Id { get; set; }
public Logo Logo { get; set; }
public int LogoId { get; set; }
public ICollection<Attachement> Attachements { get; set; } = new List<Attachement>();
}
public DbSet<Company> Companies { get; set; }
public DbSet<Attachement> Attachements { get; set; }
public DbSet<Logo> Logos { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<File>().HasDiscriminator();
}
The design you have right now is error prone, I assume one file cannot be both Logo and Attachment which means one of the two Company navigation properties will always be null. You will have to do null checks everywhere in the system
You should have separate models for File and Logo
I have the following:
public class Event : IEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("description")]
public string Description { get; set; }
[JsonProperty("date")]
public DateTimeOffset Date { get; set; }
[JsonProperty("distance")]
public int Distance { get; set; }
[JsonProperty("verticalAscend")]
public int VerticalAscend { get; set; }
[ForeignKey("User")]
[JsonProperty("userId")]
public Guid UserId { get; set; }
//attending
public DateTimeOffset DateCreated { get; set; }
public DateTimeOffset DateModified { get; set; }
[JsonProperty("user")]
public virtual User User { get; set; }
[JsonProperty("comments")]
public virtual ICollection<Comment> Comments { get; set; }
[JsonProperty("attending")]
public virtual ICollection<User> AttendingList { get; set; }
}
And:
public class User : IEntity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[JsonProperty("id")]
public Guid Id { get; set; }
[JsonProperty("profilePicUrl")]
public string ProfilePicUrl { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("surname")]
public string LastName { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
public DateTimeOffset DateCreated { get; set; }
public DateTimeOffset DateModified { get; set; }
public virtual ICollection<Event> AttendingEvents { get; set; }
[InverseProperty("User")]
public virtual ICollection<Event> Events { get; set; }
}
Relationships:
Event:
Many Users attending (AttendingList)
User:
Can attend many events (AttendingEvents)
Can create multiple events (Events)
There exists a many-many relationship between the Event.AttendingList and User.AttendingEvents.
There exists 0-many relationship between Event.User and User.Events, with ForeignKey as UserId.
I am trying to configure these with Fluent API, and using the InverseProperty to configure the other side of the relationship, mapping back to Event.User, but getting the following error:
Introducing FOREIGN KEY constraint 'FK_dbo.UserEvents_dbo.Events_Event_Id' on table 'UserEvents' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I am unsure on how to solve this relationship on one table. What am I doing wrong?
In the DbContext configure your models as follows:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Event>().HasRequired(e => e.User)
.WithMany(u => u.Events)
.HasForeignKey(e => e.UserId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasMany<Event>(s => s.AttendingEvents)
.WithMany(c => c.AttendingList)
.Map(cs =>
{
cs.MapLeftKey("UserId");
cs.MapRightKey("EventId");
cs.ToTable("UserEvents");
});
}
So I'm following this answer in trying to get two foreign keys to get to a single table and it works.
public class Team
{
public int TeamId { get; set;}
public string Name { get; set; }
public virtual ICollection<Match> HomeMatches { get; set; }
public virtual ICollection<Match> AwayMatches { get; set; }
}
public class Match
{
public int MatchId { get; set; }
public int HomeTeamId { get; set; }
public int GuestTeamId { get; set; }
public float HomePoints { get; set; }
public float GuestPoints { get; set; }
public DateTime Date { get; set; }
public virtual Team HomeTeam { get; set; }
public virtual Team GuestTeam { get; set; }
}
public class Context : DbContext
{
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Match>()
.HasRequired(m => m.HomeTeam)
.WithMany(t => t.HomeMatches)
.HasForeignKey(m => m.HomeTeamId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Match>()
.HasRequired(m => m.GuestTeam)
.WithMany(t => t.AwayMatches)
.HasForeignKey(m => m.GuestTeamId)
.WillCascadeOnDelete(false);
}
}
However, in my solution I don't want Team to have HomeMatches and AwayMatches collections as it doesn't make sense to be able to navigate to Match from that entity.
Is it possible to have two foreign keys pointing to the same table when the entity for the parent table doesn't have collections for the child tables.
I would like my Team entity to be like below.
public class Team
{
public int TeamId { get; set;}
public string Name { get; set; }
// HomeMatches and AwayMatches collection is no longer here
}
How could I use the modelBuilder to articulate to EntityFramework that I want to HomeTeamID and GuestTeamID to be foreign keys of Team?
Just remove collections & leave empty parameters for .WithMany().
Here's my code:
base class:
public class BaseEnt
{
[Key]
public int ID { get;set; }
public DateTime InsertDate { get; set; }
public int InsertUserID { get; set; }
public DateTime UpdateDate { get; set; }
public int UpdateUserID { get; set; }
[Timestamp]
public byte[] Timestamp { get; set; }
public virtual User InsertUser { get; set; }
public virtual User UpdateUser { get; set; }
}
user entity:
public class User:BaseEnt
{
public string Username { get; set; }
public string Fullname { get; set; }
public virtual ICollection<User> InsertedUsers { get; set; }
public virtual ICollection<User> UpdatedUsers { get; set; }
}
model creating:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasRequired(t => t.InsertUser)
.WithMany(t=>t.InsertedUsers)
.HasForeignKey(t => t.InsertUserID).WillCascadeOnDelete(false);
modelBuilder.Entity<User>()
.HasRequired(t => t.UpdateUser)
.WithMany(t=>t.UpdatedUsers)
.HasForeignKey(t => t.UpdateUserID).WillCascadeOnDelete(false);
}
Seed:
protected override void Seed(MCFDataContext context)
{
context.Users.Add(new Entities.User {ID=1,Fullname="Rusty Boi ",Username="jhaskdhaksdhk",InsertUserID=1,UpdateUserID=1,UpdateDate=DateTime.Now,InsertDate=DateTime.Now });
}
and here's the error i encountered in the seed part:
{"Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values."}
what you have here is some sort of "chicken and egg" problem. Possible solutions:
Make the foreign keys InsertUserID and UpdateUserID nullable
Do a design change: Maybe the user table shouldn't have InsertUserID, UpdateUserID. this could be solved by introducing another base class which doesn't have the InsertUserID and UpdateUserID properties or something similar
I'm trying to model a simple QA application using code first for learning purposes. Users should be able to ask questions, answer questions and write comments for both questions and answers. Here is my model classes:
[Table("UserProfile")]
public class UserProfile
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int UserId { get; set; }
public string UserName { get; set; }
public DateTime? BirthDate { get; set; }
public ICollection<Gym> Gyms { get; set; }
}
[Table("Question")]
public class Question
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int QuestionId { get; set; }
public int UserProfileId { get; set; }
[ForeignKey("UserProfileId")]
public UserProfile UserProfile { get; set; }
public string Header { get; set; }
public string Content { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreateDate { get; set; }
public ICollection<Answer> Answers { get; set; }
public ICollection<QuestionComment> QuestionComments { get; set; }
}
[Table("QuestionComment")]
public class QuestionComment
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int QuestionCommentId { get; set; }
public int UserProfileId { get; set; }
[ForeignKey("UserProfileId")]
public UserProfile UserProfile { get; set; }
public int QuestionId { get; set; }
[ForeignKey("QuestionId")]
public Question Question { get; set; }
[Column("Content", TypeName = "ntext")]
public string Content { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreateDate { get; set; }
}
[Table("Answer")]
public class Answer
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int AnswerId { get; set; }
public int UserProfileId { get; set; }
[ForeignKey("UserProfileId")]
public UserProfile UserProfile { get; set; }
public int QuestionId { get; set; }
[ForeignKey("QuestionId")]
public Question Question { get; set; }
[Column("Content", TypeName="ntext")]
public string Content { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreateDate { get; set; }
public IList<AnswerComment> AnswerComments { get; set; }
}
[Table("AnswerComment")]
public class AnswerComment
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int AnswerCommentId { get; set; }
public int UserProfileId { get; set; }
[ForeignKey("UserProfileId")]
public UserProfile UserProfile { get; set; }
public int AnswerId { get; set; }
[ForeignKey("AnswerId")]
public Answer Answer { get; set; }
[Column("Content", TypeName = "ntext")]
public string Content { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public DateTime CreateDate { get; set; }
}
And here is my db context class:
public class TestDbContext : DbContext
{
public TestDbContext()
: base("DefaultConnection")
{
}
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Question> Questions { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// After update
modelBuilder.Entity<UserProfile>()
.HasMany(p => p.Questions)
.WithRequired()
.HasForeignKey(c => c.UserProfileId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<UserProfile>()
.HasMany(p => p.Answers)
.WithRequired()
.HasForeignKey(c => c.UserProfileId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<UserProfile>()
.HasMany(p => p.AnswerComments)
.WithRequired()
.HasForeignKey(c => c.UserProfileId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<UserProfile>()
.HasMany(p => p.QuestionComments)
.WithRequired()
.HasForeignKey(c => c.UserProfileId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Question>()
.HasMany(p => p.Answers)
.WithRequired()
.HasForeignKey(c => c.QuestionId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Question>()
.HasMany(p => p.QuestionComments)
.WithRequired()
.HasForeignKey(c => c.QuestionId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Answer>()
.HasMany(p => p.AnswerComments)
.WithRequired()
.HasForeignKey(c => c.AnswerId)
.WillCascadeOnDelete(false);
// After update
}
}
I'm getting the following error when creating db using the above declarations:
Introducing FOREIGN KEY constraint 'AnswerComment_UserProfile' on table 'AnswerComment' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.\r\nCould not create constraint. See previous errors.
What do I have to do to fix this?
Thanks in advance,
Try adding this to your UserProfile entity:
public virtual ICollection<AnswerComment> AnswerComments { get; set; }
And then add this to your modelBuilder, this should get rid off the error in your question, and you will probably have to do this for QuestionComment, Question, and Answer:
modelBuilder.Entity<AnswerComment>()
.HasRequired(u => u.UserProfile)
.WithMany(a => a.AnswerComments)
.WillCascadeOnDelete(false);
You have a table A that contains a FK to another table B.
In the same time table A contains a FK to table C.
Now, both tables B and C contains FKs to table D.
If all FKs are defined as delete cascade a record in table C can be deleted twice.
This is not a logic problem, but SQL Server does not support this option.
To avoid this problem set on delete no action.