I'm creating a Razor Pages application that resembles a "hockey league". As I'm still grasping the concept of foreign/primary keys, I'm not quite sure if I'm setting up my data model correctly. After attempting to update my database after a migration I am getting the following error that has led me to believe I didn't set them up correctly:
Introducing FOREIGN KEY constraint 'FK_Team_Division_DivisionID' on table 'Team' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Based on these three entities, am I clearly doing something wrong?
public class Team
{
public int ID { get; set; }
public int? CoachID { get; set; }
public int? DivisionID { get; set; }
public int? ConferenceID { get; set; }
[Display(Name = "Team")]
public string TeamName { get; set; }
[Display(Name = "Location")]
public string TeamLocation { get; set; }
public Coach Coach { get; set; }
public Division Division { get; set; }
public Conference Conference { get; set; }
public ICollection<Player> Players { get; set; }
}
public class Conference
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ID { get; set; }
[Display(Name = "Conference")]
public string ConferenceName { get; set; }
public ICollection<Division> Divisions { get; set; }
public ICollection<Team> Teams { get; set; }
}
public class Division
{
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int ID { get; set; }
public int ConferenceID { get; set; }
[Display(Name = "Division")]
public string DivisionName { get; set; }
public Conference Conference { get; set; }
public ICollection<Team> Teams { get; set; }
}
My idea is that every Team will belong to a Conference and a Division. There can be many Teams in a Division, and many Divisions in a Conference.
The problem you're running into is that SQL server doesn't know how to handle a Delete of an item that has multiple parents. You'll need to help it out a bit. Choose a route that you want Team to be deleted on, for instance:
Conference --> Division --> Team
Then you must determine the routes that you don't want it to be deleted on, for instance:
Conference --> Team
Once you've decided which routes won't be used for deletion, you can specify it in the OnModelCreating(DbModelBuilder modelBuilder) method for your context
modelBuilder.Entity<Conference>()
.HasRequired(x => x.Team)
.WithMany()
.WillCascadeOnDelete(false);
EDIT
Pretty sure I got that backwards above, try this:
modelBuilder.Entity<Team>()
.HasRequired(x => x.Conference)
.WithMany()
.WillCascadeOnDelete(false);
Related
I am trying to build an organization hierarchy where each team might contain one or many members and/or one or many sub-teams.
To do so, my model is:
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Members { get; set; }
public virtual ICollection<Team> SubTeams { get; set; }
public Employee Manager { get; set; }
}
When adding a migration and updating database, everything seems logical in the table.
EF has added an extra nullable column "Team_Id" where the Id of the parent Team gets stored.
My question is about getting the Id of the parent Team from my model.
I tried adding:
public int? Team_Id
To my model, but EF considered it as a model change and asked for another migration.
How can I get the value of column Team_Id in my model? getting this info takes too much processing when looping through teams.
I always add foreign key in my model. When it adds to the model, EF won't add Team_Id .
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Members { get; set; }
public virtual ICollection<Team> SubTeams { get; set; }
public Employee Manager { get; set; }
public int? ParentId { get; set; }
[ForeignKey("ParentId")]
public Team ParentTeam { get; set; }
}
I hope this example be helpful.
I'm trying to do a Code First migration, but one of the models/tables behave pretty weird when I migrate.
Team and Tournament makes a new Table to reference what team belongs to what tournament and the other way around - That's totally what I want.
I'm trying to do the same with Matchup and Team, defining collections for both, but for some reason it makes a single property, TeamId, in Matchup which is a problem since a Matchup should be able to store more than one Team.
Screenshots for clarity
Thanks in advance.
You need to tell EF how to do the relationships when you have multiple references in the same file. I prefer fluent code for this:
Fix models:
public class Matchup
{
public int Id { get; set; }
public int WinnerId { get; set; } // FK by convention
public Team Winner { get; set; }
public Tournament Tournament { get; set; }
public ICollection<Team> Teams { get; set; }
}
public class Team
{
public int Id { get; set; }
public ICollection<Player> Players{ get; set; }
public ICollection<Matchup> Matchups{ get; set; }
public ICollection<Matchup> MatchupWinners{ get; set; }
public ICollection<Tournament> Tournaments{ get; set; }
}
// Configure 1 to many
modelBuilder.Entity<Matchup>()
.HasOptional(m => m.Winner)
.WithMany(p => p.MatchupWinners)
.HasForeignKey(p => p.WinnerId);
// Configure many to many
modelBuilder.Entity<Matchup>()
.HasMany(s => s.Teams)
.WithMany(c => c.Matchups)
.Map(t =>
{
t.MapLeftKey("MatchupId");
t.MapRightKey("TeamId");
t.ToTable("MatchupTeam");
});
But you can also do it with annotations:
public class Team
{
public int Id { get; set; }
public ICollection<Player> Players{ get; set; }
[InverseProperty("Teams")]
public ICollection<Matchup> Matchups{ get; set; }
[InverseProperty("Winner")]
public ICollection<Matchup> MatchupWinners{ get; set; }
public ICollection<Tournament> Tournaments{ get; set; }
}
I have one table "Adverts" which stores basic info about adverts (eg: Name, Excerpt, Creation date...), and I need to store more detailed info in a separate table, But, here's my problem. Adverts can be different by type (sell, buy, rent, ...), category (residential, commercial, ...), so, detailed info is also different (eg: Commercial Advert don't need kitchen area property). I want to make few models which will describe detailed info for specific type or category
Here's my Adverts model:
[Table("Adverts_Adverts")]
public class Advert {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid AdvertId { get; set; }
public virtual Metadata Metadata { get; set; }
[Required]
[DataType(DataType.Text)]
public String Name { get; set; }
[DataType(DataType.Html), AllowHtml]
public String Content { get; set; }
[ForeignKey("Section")]
public Guid SectionId { get; set; }
public virtual Section Section { get; set; }
[ForeignKey("Category")]
public Guid CategoryId { get; set; }
public virtual Category Category { get; set; }
[ForeignKey("Type")]
public Guid TypeId { get; set; }
public virtual Type Type { get; set; }
public Decimal Price { get; set; }
[DataType("Enum")]
public Currency Currency { get; set; }
[ForeignKey("Details")]
public Guid DetailsId { get; set; }
public virtual ?????????? Details { get; set; }
[ForeignKey("User")]
public String UserId { get; set; }
public virtual User User { get; set; }
[ReadOnly(true)]
[DataType(DataType.DateTime)]
public DateTime Added { get; set; }
[ReadOnly(true)]
[DataType(DataType.DateTime)]
public DateTime Updated { get; set; }
public Int32 Views { get; set; }
[ReadOnly(true)]
public Status Status { get; set; }
...
}
here's my detailed info model for residential adverts:
[Table("Adverts_Details")]
public class ResidentialDetails {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid DetailsId { get; set; }
[ForeignKey("Advert")]
public Guid AdvertId { get; set; }
public virtual Advert Advert { get; set; }
[Required]
public Int32 Storeys { get; set; }
[Required]
public Int32 Floor { get; set; }
[Required]
public Int32 Rooms { get; set; }
[Required]
public Decimal TotalArea { get; set; }
[Required]
public Decimal LivingArea { get; set; }
[Required]
public Decimal KitchenArea { get; set; }
...
}
and this may be for commercial adverts:
[Table("Adverts_Details")]
public class CommercialDetails {
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid DetailsId { get; set; }
[ForeignKey("Advert")]
public Guid AdvertId { get; set; }
public virtual Advert Advert { get; set; }
[Required]
public Int32 OfficesCount { get; set; }
[Required]
public Int32 Floor { get; set; }
[Required]
public Decimal TotalArea { get; set; }
...
}
So, how can I access both, ResidentialDetails and CommercialDetails, data within advert's property "Details"?
(Thank in advance)
This is an architecture problem, which is hard to answer without a complete understanding of your business rules. I can give you some general advice that will hopefully help you along.
As much as possible, remove complexity. I'm not sure what a "kitchen area property" is, but can you generalize it at all? Based upon context, you can call it something different, use it differently, etc. but if it's just a text field, then you can repurpose it in other contexts. Maybe for a residential advert it's "kitchen area" while maybe for commercial it's "break room area". (I really have no idea what this property is for, but I'm just trying to make the point that the same property can have a similar but slightly different meaning in different contexts).
If you can't generalize, then you'll need to start working on inheritance strategies. Create an object graph. How are these types and categories of adverts related. How are they different. Which ones are supergroups of others, etc.? Again, I don't know anything about the business rules at play, but maybe you need classes like Advert, ResidentialAdvert : Advert and CommercialAdvert : Advert. Then, you can add additional properties to these subclasses as necessary.
You'll also need to decide on a relational strategy. By default, EF will implement simple inheritance as STI (single-table inheritance, aka table per hierarchy or TPH for short). In other words, with the classes above, you would end up with an Adverts table with a Discriminator column. The value for this column would be one of "Advert", "ResidentalAdvert", or "CommercialAdvert", indicating which class should be instantiated, but all of the columns for all of the subclasses would reside in the same table. The benefit is that no joins are necessary, but the detriment is that all additional columns on your subclasses must be nullable or have default values. Other possible strategies would include, table per type (TPT), a compositional strategry, or table per concrete type (TPC), where every subtype gets its own unique table with all the fields from all supertypes.
I have a relatively complex relationship I need to set up between a User object and a lot of lookup tables. The user object is your run of the mill user model:
public class Youth : IAuditInfo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public Guid YouthGuid { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Address Address { get; set; }
public DateTime CreatedDate { get; set; }
public DateTime ModifiedDate { get; set; }
public string ImageName { get; set; }
[ForeignKey("FkYouthId")]
public ICollection<User> Parents { get; set; }
public CubPack Pack { get; set; }
public virtual ICollection<RequirementsLog> RequirementsLogs { get; set; }
public Youth()
{
Parents = new List<User>();
}
}
The lookup tables is where it gets complex and I can't figure out the path of least complexity in binding them together. For the lookups it is a series of tables starting with one 'master' table, that rolls down hierarchically to requirements and sub requirements, like this:
Master:
public class BearTrail
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<BearTrailRequiredBadge> BearTrailRequiredBadges { get; set; }
public virtual ICollection<BearTrailElectiveBadge> BearTrailElectivedBadges { get; set; }
}
Required Badges:
public class BearTrailRequiredBadge
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public int Number { get; set; }
public string Description { get; set; }
public virtual ICollection<BearTrailRequiredBadgeSubRequirement> BearTrailRequiredBadgeSubRequirements { get; set; }
}
Required Badge sub requirement:
public class BearTrailRequiredBadgeSubRequirement
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Number { get; set; }
public string Text { get; set; }
public bool Required { get; set; }
}
This is one set of the lookups, there are about four nested classes like this, and some one off tables as well. Total lookup tables is about 16, give or take.
I was initially thinking if using my RequirementLog model to bind it:
public class RequirementsLog
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public virtual ICollection<Youth> Youth { get; set; }
public BearTrail BearTrailRequirements { get; set; }
public TigerTrail TigerTrailRequirements { get; set; }
public WolfTrail WolfTrailRequirements { get; set; }
public WebelosTrail WebelosTrailRequirements { get; set; }
public WebelosArrowOfLight WebelosArrowOfLightRequirements { get; set; }
}
So there is a many to many between RequirementsLog and Youth. The table created out of RequirementsLog has one PK column (ID), and FK columns for each property. The many to many table created out of this (RequirementsLogYouths) has two PKs (RequirementsLogId, and YouthId).
Am I going about this the right way? The end goal is to have the 16 or so tables server as just lists of various requirements, and have another table(s) to track a particular youths progress through the requirements. I have a hard time visualizes some of this DBA stuff, so any input would be greatly appreciated.
In most cases, a requirements "log" be in a one (people) to many (the log).
Unless... One logged item is for many kids...
If so, the you need a third table, that maps many people to multiple logged events. That is, if this is truly a many to many. In general, that situation almost always begs for a third, intermediate mapping table. Read up a bit on many to many designs, and you'll quickly see it, and how simple it is.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Entity1>()
.HasMany(b => b.Entities2)
.WithMany(p => p.Entities1)
.Map(m =>
{
m.ToTable("Entitie1Entity2");
m.MapLeftKey("Entity1Id");
m.MapRightKey("Entity2Id");
});
}
I am a bit confused as to why I am getting this error:
Introducing FOREIGN KEY constraint 'FK_QuestionTerms_Terms_TermId'
on table 'QuestionTerms' 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 have a class Question and a class Term, Questions may have any number of Terms associated with them, and Terms may have any number of Questions associated with them. So I am attempting to create a many to many relationship between the two. First I attempted to use convention, and I am allowing EntityFramework to create the database. This is the Question class
public class Question
{
public Guid Id { get; set; }
public int QuestionNumber { get; set; }
public string StatementHtml { get; set; }
public string AnswerHeaderHtml { get; set; }
public string NotesHtml { get; set; }
public Guid CategoryId { get; set; }
public Guid CourseId { get; set; }
public Guid QuestionTypeId { get; set; }
public Guid? SimulationId { get; set; }
public Guid? SimulationTabId { get; set; }
public ICollection<Term> Terms { get; set; }
public ICollection<ReferenceItem> ReferenceItems { get; set; }
}
And here is the Term Class
public class Term
{
public Guid Id { get; set; }
public string Name { get; set; }
public string StatementHtml { get; set; }
public string Authority { get; set; }
public Guid ProductId { get; set; }
public Product Product { get; set; }
public ICollection<Question> Questions { get; set; }
}
I have also attempted to override OnModelCreating as follows, both process result is the exact same error code.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Question>()
.HasMany(q => q.Terms)
.WithMany(t => t.Questions)
.Map(x =>
{
x.MapLeftKey("QuestionId");
x.MapRightKey("TermId");
x.ToTable("QuestionTerms");
});
}
The problem is that a cacade delete would go back and forth between the tables.
For example first deleting term A which would delete question 1,2 and 3. Question 1 was also used in term B so term B must be deleted .....
It therefore stops you creating such constraints.
There is a good coverage of how to fix it here: Entity Framework 4.1 InverseProperty Attribute and ForeignKey
Edit
This could be a side effect of other problems. You should start with a much simpler model and then gradually build it up.
For example:
Why do you have both ProductId and product
Why CategoryId and not Category
...
Try adding it in your OnModelCreating() method
modelBuilder.Entity<Question>().HasRequired(oo => oo.Term).WithMany(oo => oo.Questions).WillCascadeOnDelete(false);