I have an issue.
I have some classes that look like this:
public class Question
{
public int Id { get; set; }
[Required] [MaxLength(255)] public string Text { get; set; }
public int CriteriaId { get; set; }
public int QuestionGroupId { get; set; }
public QuestionGroup QuestionGroup { get; set; }
public virtual IList<Answer> Answers { get; set; }
public virtual Criteria Criteria { get; set; }
}
public class Answer
{
public int Id { get; set; }
[Required] [MaxLength(255)] public string Text { get; set; }
public int QuestionId { get; set; }
public Question Question { get; set; }
public virtual IList<State> States { get; set; }
}
public class Criteria
{
public int Id { get; set; }
[Required] [MaxLength(100)] public string Name { get; set; }
public virtual IList<State> States { get; set; }
public IList<Question> Questions { get; set; }
}
public class State
{
public int Id { get; set; }
public int CriteriaId { get; set; }
[Required] [MaxLength(100)] public string Name { get; set; }
public int Score { get; set; }
public virtual IList<Filter> Filters { get; set; }
public Criteria Criteria { get; set; }
}
They have a many to many relationship, so I created this mapping:
modelBuilder.Entity<Answer>()
.HasMany(m => m.States)
.WithMany()
.Map(m => {
m.MapLeftKey("AnswerId");
m.MapRightKey("StateId");
m.ToTable("AnswerStates");
});
When I tried to update my database I get an error about foreign keys.
So I added this line (as a temporary fix):
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
But I need the cascading delete.
I can't seem to figure out why it won't let me have it.
The error I get when trying to include cascading deletes is:
Introducing FOREIGN KEY constraint 'FK_dbo.AnswerStates_dbo.States_StateId' on table 'AnswerStates' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I hope I have provided enough information. Does anyone know what I can do to allow the cascading delete (I need to be able to delete a State or an Answer and it to remove the record from AnswerStates
This is pretty much the same as another question (Deleting only one entry from Many-to-Many relationship) I was answering today. Just your case is a bit more complicated.
The multiple cascade path in your case is from Criteria to AnswerStates. When deleting a Criteria record, the AnswerStates records can be deleted either by Criteria->States->AnswerStates or Criteria->Questions->Answers->AnswerStates relationships.
The solution is always one and the same - turn one of the relationships cascade delete off and handle deletion either manually or via trigger.
In this particular case, my suggestion is to turn Criteria->States cascade delete off:
modelBuilder.Entity<Criteria>()
.HasMany(m => m.States)
.WithRequired(d => d.Criteria)
.HasForeignKey(d => d.CriteriaId)
.WillCascadeOnDelete(false);
and use something like this before deleting a Criteria:
db.States.RemoveRange(db.States.Where(s => s.CriteriaId == criteriaId_to_be_removed));
Related
I have 4 tables. Cities, Providers, ProviderAdmins, AspNetUsers.
I want to setup following behaviour:
If i delete Cities entry - City was deleted.
If i delete AspNetUsers entry - User was deleted -> ProviderAdmin was
deleted
If i delete ProviderAdmins entry - ProviderAdmin was deleted -> User was deleted
If i delete Provider entry - Provider was deleted -> ProviderAdmin
was deleted -> User was deleted.
Now i even can't create my db from my model, because getting following issue:
Introducing FOREIGN KEY constraint 'FK_ProviderAdmins_Providers_ProviderId' on table 'ProviderAdmins' 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.
My entities:
public class City
{
public long Id { get; set; }
public string Region { get; set; } = string.Empty;
public string Name { get; set; } = string.Empty;
public virtual ICollection<ProviderAdmin> ProviderAdmins { get; set; }
}
public class User : IdentityUser
{
public string LastName { get; set; }
public string MiddleName { get; set; }
public string FirstName { get; set; }
public virtual ProviderAdmin ProviderAdmin { get; set; }
}
public class Provider
{
public long Id { get; set; }
public virtual ICollection<ProviderAdmin> ProviderAdmins { get; set; }
}
public class ProviderAdmin
{
public long Id { get; set; }
[Required]
[ForeignKey("UserId")]
public string UserId { get; set; }
public virtual User User { get; set; }
[Required]
public long ProviderId { get; set; }
public virtual Provider Provider { get; set; }
public long? CityId { get; set; }
public virtual City City { get; set; }
}
Fluent Api:
builder.Entity<Provider>()
.HasMany(pa => pa.ProviderAdmins)
.WithOne(p => p.Provider)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<City>()
.HasMany(pa => pa.ProviderAdmins)
.WithOne(p => p.City)
.OnDelete(DeleteBehavior.NoAction);
builder.Entity<User>()
.HasOne(u => u.ProviderAdmin)
.WithOne(p => p.User)
.HasForeignKey<ProviderAdmin>(p => p.UserId)
.OnDelete(DeleteBehavior.Cascade);
Please, give me advice about how can i fix error mentioned above, and how can i setup behaviour which i want to get.
Thank you so much for your attention.
UPD: I made relationship PAs - AspNetUsers to be one or zero to one. It is still not working, i still missing something. I know that problem is in relationship between provideradmin and users but i really don't know why it not allowed.
I'm trying to set the Model builder cascade property but I'm not getting the syntax correct.
Simple database:
I have a class (ReqForBetaReader):
public class ReqForBetaReader
{
public ReqForBetaReader()
{
Tags = new List<TagForReqBeta>();
}
public Guid Id { get; set; }
public string Title { get; set; }
public string Synopsis { get; set; }
public int WordCount { get; set; }
public Guid AuthorId { get; set; }
public virtual User Author { get; set; }
public DateTime UpdatedOn { get; set; }
public virtual ICollection<TagForReqBeta> Tags { get; set; }
}
I have my TagForReqBeta class
public class TagForReqBeta
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public Guid ReqForBetaReaderId { get; set; }
public virtual ReqForBetaReader ReqForBetaReader { get; set; }
[Required]
public Guid UserId { get; set; }
public virtual User User { get; set; }
public DateTime AddedOn { get; set; }
}
So a ReqForBeta can have multiple tags.
When I ran Update-Database, I got the dreaded:
Introducing FOREIGN KEY constraint 'FK_TagsOnRequestForBetaReaders_RequestsForBetaReaders_ReqForBetaReaderId' on table 'TagsOnRequestForBetaReaders' 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.
I believe this means that when I delete a ReqForBeta, I should also delete all the TagForReqBeta as well, but I'm having trouble with the syntax. Any help?
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<ReqForBetaReader>()
.HasMany<TagForReqBeta>(x => x.Tags)
.WithOne<ReqForBetaReader>(x => x.ReqForBetaReaderId)
.OnDelete(DeleteBehavior.Cascade);
base.OnModelCreating(builder);
}
Since ReqForBetaReaderId is not nullable, when you delete ReqForBetaReader record you will have to delete TagForReqBeta.
Since as I understand the same tag can be used for several records you have to make ReqForBetaReaderId nullable
[Required]
public Guid? ReqForBetaReaderId { get; set; }
builder.Entity<ReqForBetaReader>()
.HasMany<TagForReqBeta>(x => x.Tags)
.WithOne<ReqForBetaReader>(x => x.ReqForBetaReaderId)
.OnDelete(DeleteBehavior.ClientSetNull);
and as #IvanStoev noticed you have the same problem with User
I'm working on an application related to the education industry, and I have the following set of classes.
public class Person : EntityBase<long>
{
public string Surname { get; set; }
public string First { get; set; }
public string Middle { get; set; }
public Gender Gender { get; set; }
public DateTime Dob { get; set; }
}
public class Student: Person
{
public string AdmissionId { get; set; }
public string StudentId { get; set; }
public List<GuardianLink> Guardians { get; set; }
public StudentStatus Status { get; set; }
}
public class Guardian: Person
{
public GuardianType GuardianType { get; set; }
public List<GuardianLink> Students { get; set; }
}
public class GuardianLink: EntityBase<long>
{
[Required]
public Student Student { get; set; }
[Required]
public Guardian Guardian { get; set; }
public Boolean IsEmergencyContact { get; set; }
public Boolean IsAllowedToPickup { get; set; }
}
When I tried to create the database from these classes, I received an error that this would result in circular cascading deletes. So I added the following.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<GuardianLink>()
.HasRequired(c => c.Guardian)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<GuardianLink>()
.HasRequired(c => c.Student)
.WithMany()
.WillCascadeOnDelete(false);
}
I expected this to resolve the issue, but I'm still getting an error.
Introducing FOREIGN KEY constraint
'FK_dbo.GuardianLinks_dbo.People_Student_Id' on table 'GuardianLinks'
may cause cycles or multiple cascade paths. Specify ON DELETE NO
ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY
constraints.
I want to configure this so that deleting a student deletes the associated links, but not the guardian, and vice versa when deleting a guardian. Why didn't this resolve the issue? What should I be doing instead?
I recently came across this strange problem with Entity Framework Code First.
My class looks like this
public class Status
{
[Key]
public int StatusID { get; set; }
public string Name { get; set; }
public int MemberID { get; set; }
[ForeignKey("MemberID")]
public virtual Member Member { get; set; }
public int PosterID { get; set; }
[ForeignKey("PosterID")]
public virtual Member Poster { get; set; }
public virtual ICollection<StatusLike> StatusLikes { get; set; }
public virtual ICollection<StatusComment> StatusComments { get; set; }
}
My Member class looks like this
public class Member
{
[Key]
public int MemberID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Bio { get; set; }
public virtual ICollection<MemberCourseTaken> MemberCourseTakens { get; set; }
public virtual ICollection<Status> Statuses { get; set; }
public virtual ICollection<Club> FoundedClubs { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
public string Phone { get; set; }
public int AccountSourceID { get; set; }
public AccountSource AccountSource { get; set; }
public int AddressID { get; set; }
public Address Address { get; set; }
public string ProfilePhoto { get; set; }
public int MemberRankID { get; set; }
public MemberRank MemberRank { get; set; }
public DateTime Created { get; set; }
public DateTime Modified { get; set; }
}
And for whatever reason the database table that is created has the following columns
StatusID
Name
MemberID
PosterID
Member_MemberID
with MemberID, PosterID, and Member_MemberID being foreign keys.
How can I keep Member_MemberID from being generated?
Your Member_MemberID column is created because of the Member.Statuses property. I can imagine that this is not what you want. Probably members and statuses should exist independent of each other, so you need a junction table.
I don't know if you already use the OnModelCreating override of the DbContext, but that's the place to change the mapping between Member and Status:
protected override void OnModelCreating(DbModelBuilder mb)
{
mb.Entity<Member>().HasMany(m => m.Statuses).WithMany();
}
This will create a table MemberStatuses table with the two Id columns as foreign keys. This is a way to model a many-to-many relationship without a navigation property on the "other" side of the association. (I don't think you want a Members property in Status).
I've seen this before. In my case (Using EF 6.1), it was because my Fluent API Mapping was set up like so:
// In my EntityTypeConfiguration<Status>
HasRequired(x => x.Member).WithMany().HasForeignKey(x => x.MemberID);
That code works perfectly fine, but it doesn't tell EF that my Member class's Collection Navigational Property Status ha been taken into account. So, while I explicitly handled the existence of a Member Navigational Property in my Status Class, I now left an orphaned related collection property. That orphaned property, being a collection, tells EF that my Status class needs to have a Foreign Key to it. So it creates that on the Status Class.
To fix it, I had to be 100% explicit.
HasRequired(x => x.Member).WithMany(x => x.Statuses).HasForeignKey(x => x.MemberID)
It could bee that your Statuses Collection property in Member needs an attribute telling it that it is already considered, and not to go auto-creating mappings. I don't know that attribute.
my code like below
public class User
{
public int ID { get; set; }
public int BillingAddressID { get; set; }
public Address BillingAddress { get; set; }
public IList<Shipment> Shipments { get; set; }
}
public class Address
{
public int ID { get; set; }
public string Street { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
}
public class Shipment
{
public int ID { get; set; }
public string State { get; set; }
public int DeliveryAddressID { get; set; }
public Address DeliveryAddress { get; set; }
public User ShipUser { get; set; }
//[ForeignKey("ShipUser")]
public int ShipUserID { get; set; }
//public int UserId { get; set; }
}
public class TestContext : DbContext
{
public DbSet<User> Users { get; set; }
public DbSet<Address> Addresses { get; set; }
public DbSet<Shipment> Shipments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Shipment>().HasRequired(u => u.ShipUser)
.WithMany(d => d.Shipments)
.HasForeignKey(c => c.ShipUserID)
.WillCascadeOnDelete(false);
}
}
if i remove the override method,i will get an error "SqlException: Introducing FOREIGN KEY constraint 'FK_Shipments_Users_ShipUserID' on table 'Shipments' 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."
if i remove ShipUserID in Shipment Class,it will work ok,when i see the table that is created by ef,i found a column named Shipment_UserID in table Shipment.I don`t know why.
if rename the class indenty key to UserID,it also work ok.
I try it anyway,but I don`t know the reason, I need some books about EF associations.
If you don't have mapping specified without cascadeDelete=false for one relationship it will create multiple cascade paths if you have tow relationships to user from Shipment.
By convention you can use public
Public User ShipUser { get; set; }
public int ShipUserID { get; set; }
it will use ShipUserID as foreign key by convention.
If you remove ShipUserID Ef need to create his own foreign key to keep the relationship . that is your ' Shipment_UserID'
rename the class indenty key to UserID I don't understand what you meant.
Here is a good tutorial to start with