I have a table with multiple fields that are foreign keys to a primary key in another table. For example :
Fixture Id (PK)
HomeTeamId (FK to Team.TeamId)
AwayTeamId (FK to Team.TeamId)
HomeTeamCoachId (FK to Coach.CoachId)
AwayTeamCoachId (FK to Coach.CoachId)
Would it be better to separate this data into 2 tables HomeTeam and AwayTeam with a foreign key to FixtureId? This is currently what was generated by Entity Framework :
FixtureId PK
HomeTeamId int
AwayTeamId int
HomeTeamCoachId int
AwayTeamCoachId int
AwayTeam_TeamId FK
HomeTeam_TeamId FK
AwayTeamCoach_CoachId FK
HomeTeamCoach_CoachId FK
This was generated through this class :
public partial class Fixture
{
public int FixtureId { get; set; }
//foreign key
public int AwayTeamId { get; set; }
//navigation properties
public virtual Team AwayTeam { get; set; }
//foreign key
public int HomeTeamId { get; set; }
//navigation properties
public virtual Team HomeTeam { get; set; }
//foreign key
public int AwayCoachId { get; set; }
//navigation properties
public virtual Coach AwayCoach { get; set; }
//foreign key
public int HomeCoachId { get; set; }
//navigation properties
public virtual Coach HomeCoach { get; set; }
}
Can anybody tell me if this is the correct way to do this?
EDIT : In reply to Slauma
So my classes would basically look like this? Or does the configuration in OnModelCreating mean I don't need some of the foreign key related code in my Fixture class?
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Entity Type Configuration
modelBuilder.Configurations.Add(new TeamConfiguration());
modelBuilder.Configurations.Add(new CoachConfiguration());
modelBuilder.Configurations.Add(new FixtureConfiguration());
modelBuilder.Entity<Fixture>()
.HasRequired(f => f.AwayTeam)
.WithMany()
.HasForeignKey(f => f.AwayTeamId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Fixture>()
.HasRequired(f => f.HomeTeam)
.WithMany()
.HasForeignKey(f => f.HomeTeamId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Fixture>()
.HasRequired(f => f.AwayCoach)
.WithMany()
.HasForeignKey(f => f.AwayCoachId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Fixture>()
.HasRequired(f => f.HomeCoach)
.WithMany()
.HasForeignKey(f => f.HomeCoachId)
.WillCascadeOnDelete(false);
}
public partial class Fixture
{
public int FixtureId { get; set; }
public string Season { get; set; }
public byte Week { get; set; }
//foreign key
public int AwayTeamId { get; set; }
//navigation properties
public virtual Team AwayTeam { get; set; }
//foreign key
public int HomeTeamId { get; set; }
//navigation properties
public virtual Team HomeTeam { get; set; }
//foreign key
public int AwayCoachId { get; set; }
//navigation properties
public virtual Coach AwayCoach { get; set; }
//foreign key
public int HomeCoachId { get; set; }
//navigation properties
public virtual Coach HomeCoach { get; set; }
public byte AwayTeamScore { get; set; }
public byte HomeTeamScore { get; set; }
}
Apparently EF doesn't detect your int properties like AwayTeamId as the foreign key for the navigation properties like AwayTeam because the primary key property in Team is not Id but TeamId. It would probably detect the FKs if they are named like AwayTeamTeamId or if the primary key property in Team has the name Id.
If you don't want to change those property names according to EF convention you can define the FKs with data annotations:
[ForeignKey("AwayTeam")]
public int AwayTeamId { get; set; }
public virtual Team AwayTeam { get; set; }
// the same for the other three FKs
Or Fluent API:
modelBuilder.Entity<Fixture>()
.HasRequired(f => f.AwayTeam)
.WithMany()
.HasForeignKey(f => f.AwayTeamId)
.WillCascadeOnDelete(false);
// the same for the other three FKs
I have disabled cascading delete because it will be enabled by default for a required relationship. But because you have two required relationships to the Team table (and the Coach table as well) it would result in two cascading delete paths from Fixture to Team and Coach. Multiple cascading delete paths are forbidden in SQL Server, so you must disable cascading delete for at least one of the two relationships between Fixture and Team (and between Fixture and Coach).
I tried out this way & working
Primary key Table
public class TravelCity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int CityId { get; set; }
public string CityName { get; set; }
public string CityDesc { get; set; }
public string Status { get; set; }
}
Table Having Foreign Key
public class TravelDetails
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Int64 TravelId { get; set; }
public Int32 FromLocation { get; set; }
[ForeignKey("FromLocation"),InverseProperty("CityId")]
public virtual TravelCity TravelCityFrom { get; set; }
public Int32 ToLocation { get; set; }
[ForeignKey("ToLocation"), InverseProperty("CityId")]
public virtual TravelCity TravelCityTo { get; set; }
public Int32 CurrentCity { get; set; }
[ForeignKey("ToLocation"), InverseProperty("CityId")]
public virtual TravelCity TravelCityCurrent{ get; set; }
}
Try out this way it will surly work..
Cheers:)
Related
I have two entities in my Core MVC application. There are two Bolge Id in the BolgeToBolge entity; one of them is FromBolgeFk, the other one is ToBolgeFk.
After update-database command in package manager console of Visual Studio 2019, I have an error like this:
Introducing FOREIGN KEY constraint 'FK_BolgeToBolge_Bolge_ToBolgeFk' on table 'BolgeToBolge' 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 codes:
Bolge.cs Entity Model:
public class Bolge
{
[Key]
public int Id { get; set; }
[Display(Name ="Bölge")]
[StringLength(100)]
[Required]
public string BolgeAd { get; set; }
[ForeignKey("Ilce")]
public int IlceFk { get; set; }
public virtual Ilce Ilce { get; set; }
public virtual ICollection<BolgeToBolge> BolgeToBolgeFroms { get; set; }
public virtual ICollection<BolgeToBolge> BolgeToBolgeToes { get; set; }
public virtual ICollection<Rezervasyon> RezervasyonsFrom { get; set; }
public virtual ICollection<Rezervasyon> RezervasyonsTo { get; set; }
}
BolgeToBolge Entity:
public class BolgeToBolge
{
[Key]
public int Id { get; set; }
[ForeignKey("FromBolge")]
[InverseProperty("BolgeToBolgeFroms")]
public int FromBolgeFk { get; set; }
public virtual Bolge FromBolge { get; set; }
[ForeignKey("ToBolge")]
[InverseProperty("BolgeToBolgeToes")]
public int ToBolgeFk { get; set; }
public virtual Bolge ToBolge { get; set; }
}
ApplicationDbContext:DbContext
{
modelBuilder.Entity<BolgeToBolge>()
.HasKey(BtB=> new {BtB.FromBolgeFk, BtB.ToBolgeFk });
modelBuilder.Entity<BolgeToBolge>()
.HasOne(BtB => BtB.FromBolge)
.WithMany(BtB => BtB.BolgeToBolgeFroms)
.HasForeignKey(BtB => BtB.FromBolgeFk).OnDelete(DeleteBehavior.Cascade);
modelBuilder.Entity<BolgeToBolge>()
.HasOne(BtB => BtB.ToBolge)
.WithMany(BtB => BtB.BolgeToBolgeToes)
.HasForeignKey(BtB => BtB.ToBolgeFk).OnDelete(DeleteBehavior.Cascade);
}
How can I solve this problem?
modelBuilder.Entity<BolgeToBolge>()
.HasOne(BtB => BtB.ToBolge)
.WithMany(BtB => BtB.BolgeToBolgeToes)
.HasForeignKey(BtB => BtB.ToBolgeFk).OnDelete(DeleteBehavior.NoAction);
I have the following tables:
Sub_Option: Sub_Option_ID as PK, Name
Sub_Option_To_Sub_Option: Sub_Option_To_Sub_Option_ID as PK, Sub_Option_ID_Primary, Sub_Option_ID_Secondary
I would like to be able to access all the secondary sub options associated with the primary sub option via EF and vice-versa. Directly using .Map won't work as the junction table Sub_Option_To_Sub_Option has a primary key.
public class Sub_Option
{
public int Sub_Option_ID { get; set; }
public string Name { get; set; }
}
corresponding to Table
CREATE TABLE Sub_Option(
Sub_Option_ID int,
Name varchar(255)
);
and Table
CREATE TABLE Sub_Option_To_Sub_Option(
Sub_Option_To_Sub_Option int PK,
Sub_Option_ID_Primary int,
Sub_Option_ID_Secondary int
);
This should work i think:
public class OptionToOption
{
[Key]
public int ID { get; set; }
[ForeignKey("PrimaryOption")]
public int PrimaryID { get; set; }
[ForeignKey("SecondaryOption")]
public int SecondaryID { get; set; }
public virtual Option PrimaryOption { get; set; }
public virtual Option SecondaryOption { get; set; }
}
public class Option
{
public Option()
{
OptionToOption = new HashSet<OptionToOption>();
}
[Key]
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<OptionToOption> OptionToOption { get; set; }
}
And in fluent api map like this (don't even think it's necessary to do this though):
modelBuilder.Entity<Option>()
.HasMany(e => e.OptionToOption)
.WithRequired(e => e.PrimaryOption)
.HasForeignKey(e => e.PrimaryID);
modelBuilder.Entity<Option>()
.HasMany(e => e.OptionToOption)
.WithRequired(e => e.SecondaryOption)
.HasForeignKey(e => e.SecondaryID);
I am quite new new to EF (basically just starting). I am having problems with following.
Lets say that I have a table that describes a product, this product (based on type) can have a number of additional properties ( for a purpose of this enquiry I will limit it to two).
class Product
{
[Key]
[Column("si_key")]
public Guid Key { get; set; }
[Column("si_Name")]
public string Name {get; set; }
[Column("si_Type")]
public TypeEnum Type { get; set; }
[Column("si_PaperType")]
public Guid? PaperType { get; set };
[Column("si_FoilType")]
public Guid? FoilType { get; set };
// Mappings
public PaperType PType { get; set; }
public FoilType FType { get; set; }
}
class FoilType
{
[Key]
[Column("ft_key")]
public Guid Key { get; set; }
[Column("ft_Name")]
public string Name {get; set; }
}
class PaperType
{
[Key]
[Column("pt_key")]
public Guid Key { get; set; }
[Column("pt_Name")]
public string Name {get; set; }
}
So really we are talking about 0-1 Relationship between Product and (paper and foilType).
How to define it using fluent API?
I was trying to use:
modelBuilder.Entity<Product>()
.HasOptional(u => u.PType)
.WithOptionalPrincipal()
.Map( m => m.MapKey("pt_guid") );
....
You can not use WithOptionalPrincipal as it implies that both sides are optional.
Configures the relationship to be optional:optional without a navigation property on the other side of the relationship. The entity type being configured will be the principal in the relationship. The entity type that the relationship targets will be the dependent and contain a foreign key to the principal.
The only option you have is so called 1-1:
class PaperType
{
[Key]
[Column("pt_key")]
public Guid Key { get; set; }
[Column("pt_Name")]
public string Name {get; set; }
// Mappings
public Product Product { get; set; }
}
modelBuilder.Entity<Product>()
.HasOptional(x => x.PType)
.WithRequired(x => x.Product);
class Product
{
[Key]
[Column("si_key")]
public Guid Key { get; set; }
[Column("si_Name")]
public string Name {get; set; }
[Column("si_Type")]
public TypeEnum Type { get; set; }
//[Column("si_PaperType")]
//public Guid? PaperType { get; set };/* remove this line*/
//[Column("si_FoilType")]
//public Guid? FoilType { get; set };/* remove this line*/
// Mappings
public PaperType PType { get; set; }
public FoilType FType { get; set; }
}
modelBuilder.Entity< Product >()
.HasOptional< u.PType >(u => u.PType)
.WithOptionalDependent(c => c.Product).Map(p => p.MapKey("PTypeId"));
Using: VS 2013, Entity Framework Code First, ASP.NET Web Project MVC
I have 2 models, in one need 2 FK for the same table:
public class A
{
public int Id { get; set; }
public string Name { get; set; }
}
public class B
{
public int Id { get; set; }
public int Id1 { get; set; }
[ForeignKey("Id1")]
public virtual A A1 { get; set; }
public int Id2 { get; set; }
[ForeignKey("Id2")]
public virtual A A2 { get; set; }
}
After enable-migration and Add-Migration Test, when I run Update-Database, I get this message:
Introducing FOREIGN KEY constraint 'FK_dbo.B_dbo.A_Id2' on table 'B' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
use this code
public class A
{
public int Id { get; set; }
public string Name { get; set; }
}
public class B
{
public int Id { get; set; }
public int Id1 { get; set; }
[ForeignKey("Id1")]
public virtual A A1 { get; set; }
public int Id2 { get; set; }
[ForeignKey("Id2")]
public virtual A A2 { get; set; }
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<B>()
.HasRequired(e => e.A1)
.WithMany()
.HasForeignKey(c => c.Id1)
.WillCascadeOnDelete(false)
.HasRequired(e => e.A2)
.WithMany()
.HasForeignKey(c => c.Id2)
.WillCascadeOnDelete(false)
;
}
alse you could use inverseProperty attribute.
I develop an asp.net mvc solution with Entity Framework Code First and I got the error:
Introducing FOREIGN KEY constraint 'FK_dbo.Transports_dbo.Shippers_ReceiverId' on table 'Transports' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
This occurred when starting the solution in the process of creation of the database.
Here are my models:
public class Transport
{
[Key]
public int Id { get; set; }
...
public int SenderId { get; set; }
public int ReceiverId { get; set; }
...
public virtual Shipper Sender { get; set; }
public virtual Shipper Receiver { get; set; }
}
public class Shipper
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public string Street { get; set; }
public string Number { get; set; }
}
If I comment the public virtual Shipper Receiver { get; set; } then it works so this is the cyclic problem.
Does anyone can help me on this problem?
For more complex 'self-bound' or 'multiple to same' relationships, you need to explicitly define the relationships - best using fluent configuration.
e.g.
modelBuilder.Entity<Transport>()
.HasRequired(at => at.Sender)
.WithMany(a => a.TransportsAsSender)
// .HasForeignKey(at => at.SenderId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Transport>()
.HasRequired(at => at.Receiver)
.WithMany(a => a.TransportsAsReceiver)
// .HasForeignKey(at => at.ReceiverId)
.WillCascadeOnDelete(false);
...that assumes you also add:
public ICollection<Transport> TransportsAsSender { get; set; }
public ICollection<Transport> TransportsAsReceiver { get; set; }
...to the Shipper
Or just use...
.WithMany() // and no collection navigation properties