Why do my one to many relationships in subclasses get a cycle error? - entity-framework

I want to have a one-to-many relation between subclasses of one master class,
and in second step use of master class for relationships with other class
please see below models:
BasePost.cs
public abstract class BasePost
{
[Key]
public long Id { get; set; }
public ICollection<Comment> Comments { get; set; }
}
Question.cs:
public class Question: BasePost
{
[Required] public string Title { get; set; }
public string Body { get; set; }
public ICollection<Answer> Answers { get; set; }
}
Answer.cs
public class Answer: BasePost
{
public string Body { get; set; }
public int Vote { get; set; }
public Question Question { get; set; }
public long QuestionId { get; set; }
}
Comment.cs
public class Comment
{
[Key]
public long Id { get; set; }
public string Text { get; set; }
public BasePost BasePost { get; set; }
public long BasePostId { get; set; }
public DateTime CreateDate { get; set; }
}
and finally in ApplicationDbContext.cs :
builder.Entity<Answer>()
.HasOne(x => x.Question)
.WithMany(a => a.Answers)
.HasForeignKey(k => k.QuestionId)
.OnDelete(DeleteBehavior.Cascade);
builder.Entity<Comment>()
.HasOne(x => x.BasePost)
.WithMany(a => a.Comments)
.HasForeignKey(k => k.BasePostId)
.OnDelete(DeleteBehavior.ClientSetNull);
but after migration and update database I get below error:
Introducing FOREIGN KEY constraint 'FK_BasePosts_BasePosts_QuestionId' on table 'BasePosts' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.

Related

Table Id field automatically append with table name that produce invalid column name

I have table Categories With Id column when insert occur is shows it shows errorSqlException: Invalid column name 'CategoriesId'.
public partial class Categories
{
public Categories()
{
CategoryTabs = new HashSet<CategoryTabs>();
}
public int Id { get; set; }
public string Title { get; set; }
public int? ParentId { get; set; }
public int? SeasonId { get; set; }
public int? Levels { get; set; }
public string Image { get; set; }
public string Description { get; set; }
public virtual Seasons Season { get; set; }
public List<Categories> children { get; set; }
public virtual ICollection<CategoryTabs> CategoryTabs { get; set; }
}
public partial class CategoryTabs
{
public int Id { get; set; }
public int? CategoryId { get; set; }
public int? TabId { get; set; }
public virtual Categories Category { get; set; }
public virtual Tabs Tab { get; set; }
}
CategoriesId is the conventional name for the Foreign Key property/column associated with the one-to-many self relationship introduced by
public List<Categories> children { get; set; }
collection navigation property inside Categories entity.
Looking at the entity model, most likely the idea was to use the ParentId for that purpose. Since it doesn't match EF Core naming conventions, it has to be mapped explicitly by using either ForeignKey data annotation:
[ForeignKey(nameof(ParentId))]
public List<Categories> children { get; set; }
or fluent API inside OnModelCreating override:
modelBuilder.Entity<Categories>()
.HasMany(e => e.children)
.WithOne()
.HasForeignKey(e => e.ParentId);

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

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?

Entity Framework Code first creates unexpected Tables and Relationships

Using EntityFramework 6.1.3, I've got the following
public class RacesContext:DbContext
{
public DbSet<Race> Races { get; set; }
public DbSet<Sailboat> Sailboats { get; set; }
public DbSet<VenueParticipation> VenueParticipations { get; set; }
}
public class Crew
{
public int CrewId { get; set; }
public string Name { get; set; }
}
public class Sailboat
{
[Key]
public int SailboatId { get; set; }
public string Name { get; set; }
public string Skipper { get; set; }
public virtual ICollection<Crew> BoatCrew { get; set; }
}
public class VenueParticipation
{
[Key]
public int Id { get; set; }
public virtual ICollection<Sailboat> Boats { get; set; }
public virtual ICollection<Race> Races { get; set; }
}
public class Race
{
[Key]
public int RaceId { get; set; }
public string Venue { get; set; }
public DateTime Occurs { get; set; }
}
EF creates the Creates the Crews table with the proper PK and FK as I would expect. But creates the Races Sailboats, VenueParticipations tables in an unexpected way. Sailboats get's the expected PK but the unexpected FK VenueParticipation_Id as does Races. I was expecting the VenueParticipations table to get FKs to the others allowing a many to many relationship.. I'm sure I'm missing something here. Any advice would be great.
You can either configure the joining tables VenueParticipationSailboat, VenueParticipationRace with the proper FKs or you can use the fluent API:
modelBuilder.Entity<VenueParticipation>()
.HasMany(t => t.Sailboats)
.WithMany(t => t.VenueParticipations)
.Map(m =>
{
m.ToTable("VenueParticipationSailboat");
m.MapLeftKey("VenueParticipationID");
m.MapRightKey("SailboatID");
});
https://msdn.microsoft.com/en-us/data/jj591620.aspx#ManyToMany

Can I avoid the foreign key properties in my entity?

I have the following classes:
public class Bicycle
{
public int BicycleId { get; set; }
public DateTime YearOfManufacture { get; set; }
public int BicycleManufactuerId { get; set; }
public BicycleManufacturer BicycleManufacturer { get; set; }
}
public class BicycleManufacturer
{
public int BicycleManufacturerId { get; set; }
public string Description { get; set; }
}
Each Bicycle must have a BicycleManufacturer (1:1). There could be some BicycleManufacturer that isn't associate with any Bicycle. Most will be associated with multiple Bicycle entities.
I have the following fluent API code to set up the FK relationship:
modelBuilder.Entity<Bicycle>()
.HasRequired(a => a.BicycleManufacturer)
.WithMany()
.HasForeignKey(u => u.BicycleManufactuerId);
This all seems to work fine. However, I would really like to remove the BicycleManufacturerId property from the Bicycle entity. It's only there to establish the FK relationship. Is there a way I can create the proper FK relationship if I remove this property?
You can remove the property and use the mapping:
modelBuilder.Entity<Bicycle>()
.HasRequired(a => a.BicycleManufacturer)
.WithMany()
.Map(m => m.MapKey("BicycleManufactuerId"));
You can also do it by convention by adding the relationship on the other side as a collection.
public class Bicycle
{
public int BicycleId { get; set; }
public DateTime YearOfManufacture { get; set; }
public int BicycleManufactuerId { get; set; }
public BicycleManufacturer BicycleManufacturer { get; set; }
}
public class BicycleManufacturer
{
public int BicycleManufacturerId { get; set; }
public ICollection<Bicycle> Bicycles { get; set; }
public string Description { get; set; }
}

How to map two properties in one code first object to the same parent type

I've been at this for hours and have tried many suggestions I found searching but no luck. I'm using code first EF 5.
The situation is that I have a class Employee. Then I have another class that has two properties on it, both are of type Employee. I want these both to be foreign key constraints but the requirements allow many of the same requests to and from the same users so I can't just use them as keys. I don't really care about Employee having the two collections for navigation but in my working through the problem that seemed a requirement. If it simplifies the problem I can remove those.
I get this message.
System.Data.Entity.Edm.EdmAssociationEnd: : Multiplicity is not valid in Role 'Employee_RequestsForEmployee_Target' in relationship 'Employee_RequestsForEmployee'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.
I've tried this using the Fluent API in the OnModelCreation method of the context;
modelBuilder.Entity()
.HasRequired(u => u.ForEmployee)
.WithMany()
.HasForeignKey(u => u.ForEmployeeId);
modelBuilder.Entity<RevenueTransferRequest>()
.HasRequired(u => u.FromEmployee)
.WithMany()
.HasForeignKey(u => u.FromEmployeeId);
The classes in conflict are (I've removed some properties for clarity);
public class Employee : IEmployee
{
[Key]
public string Id { get; set; }
[InverseProperty("ForEmployee")]
public ICollection<RevenueTransferRequest> RequestsForEmployee { get; set; }
[InverseProperty("FromEmployee")]
public ICollection<RevenueTransferRequest> RequestsFromEmployee { get; set; }
}
public class RevenueTransferRequest : IRevenueTransferRequest
{
[Key]
public Guid Id { get; set; }
[Required]
[ForeignKey("ForEmployee")]
public String ForEmployeeId { get; set; }
[InverseProperty("RequestsForEmployee")]
public Employee ForEmployee { get; set; }
[Required]
[ForeignKey("FromEmployee")]
public String FromEmployeeId { get; set; }
[InverseProperty("RequestsFromEmployee")]
public Employee FromEmployee { get; set; }
}
Any help would be much appreciated. Thanks in advance.
I never did figure out how to do it using data annotations but using the Fluent API I was able to do it. What I was missing was that I had to specify in the HasMany() method what the relationship on the other side was which I assumed was understood through the data annotations and conventions.
This is called in the DbContext OnModelCreating override (The WillCascadeOnDelete(false) is related to another issue).
modelBuilder.Entity<RevenueTransferRequest>()
.HasRequired(e => e.FromEmployee)
.WithMany(x=>x.RequestsFromEmployee)
.WillCascadeOnDelete(false);
modelBuilder.Entity<RevenueTransferRequest>()
.HasRequired(e => e.ForEmployee)
.WithMany(x => x.RequestsForEmployee)
.WillCascadeOnDelete(false);
With the classes:
[Key]
public String Id { get; set; }
public String BusinessUnitLeaderId { get; set; }
public Employee BusinessUnitLeader { get; set; }
[Required]
[MaxLength(150)]
public String DisplayName { get; set; }
public ICollection<Project> BusinessUnitLeaderProjects { get; set; }
public ICollection<RevenueTransferRequest> RequestsForEmployee { get; set; }
public ICollection<RevenueTransferRequest> RequestsFromEmployee { get; set; }
public class RevenueTransferRequest
{
[Key]
public Guid Id { get; set; }
[Required]
public String ForEmployeeId { get; set; }
public Employee ForEmployee { get; set; }
[Required]
public String FromEmployeeId { get; set; }
public Employee FromEmployee { get; set; }
[Required]
public String ProjectId { get; set; }
public Project Project { get; set; }
[Required]
public Double? TransferAmount { get; set; }
public int WorkflowState { get; set; }
}