EF 4.1 Code first multiple one-to-many associations - c#

Here is my model
public class Horse
{
public int HorseId { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public LegType LegType { get; set; }
public Character Character { get; set; }
public int Hearts { get; set; }
public bool Retired { get; set; }
// Parents
public Horse Sire { get; set; }
public Horse Dam { get; set; }
// Internals
public int Stamina { get; set; }
public int Speed { get; set; }
public int Sharp { get; set; }
// Special
public int Dirt { get; set; }
// Externals
public int Start { get; set; }
public int Corner { get; set; }
public int OutOfTheBox { get; set; }
public int Competing { get; set; }
public int Tenacious { get; set; }
public int Spurt { get; set; }
//Races
public virtual ICollection<Race> RaceResults { get; set; }
//Training
public virtual ICollection<Training> TrainingResults { get; set; }
}
public class Race
{
public int RaceId { get; set; }
public int Favorite { get; set; }
public LegType LegType { get; set; }
public int Players { get; set; }
public DateTime Split { get; set; }
public DateTime Final { get; set; }
public int Position { get; set; }
public virtual int TrackId { get; set; }
public virtual Track Track { get; set; }
public virtual int LinkedHorseId { get; set; }
public virtual Horse LinkedHorse { get;set; }
}
public class Training
{
public int TrainingId { get; set; }
public string Type { get; set; }
public string Result { get; set; }
public string Food { get; set; }
public int Start { get; set; }
public int Corner { get; set; }
public int Outofthebox { get; set; }
public int Competing { get; set; }
public int Tenacious { get; set; }
public int Spurt { get; set; }
public virtual int LinkedHorseId { get; set; }
public virtual Horse LinkedHorse { get; set; }
}
public class Track
{
public int TrackId { get; set; }
public string Name { get; set; }
public int Distance { get; set; }
public bool G1 { get; set; }
public int Prize { get; set; }
}
And here is my fluent API code.
public class HorseTracker : DbContext
{
public DbSet<Horse> Horses { get; set; }
public DbSet<LegType> LegTypes { get; set; }
public DbSet<Character> Characters { get; set; }
public DbSet<Training> TrainingResults { get; set; }
public DbSet<Track> Tracks { get; set; }
public DbSet<Race> Races { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Race>()
.HasRequired(r => r.LinkedHorse)
.WithMany(h => h.RaceResults)
.HasForeignKey(r => r.LinkedHorseId);
modelBuilder.Entity<Training>()
.HasRequired(t => t.LinkedHorse)
.WithMany(t => t.TrainingResults)
.HasForeignKey(t => t.LinkedHorseId);
modelBuilder.Entity<Race>()
.HasRequired(r => r.Track)
.WithMany()
.HasForeignKey(r => r.TrackId)
.WillCascadeOnDelete(false);
}
}
I keep getting this error:
Unable to determine the principal end of an association between the types 'DOCCL.Models.Horse' and 'DOCCL.Models.Horse'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Any clue what i'm doing wrong.
I've been playing around with no foreign keys. making one of the required lists optional.
they all result in different errors.
mostly saying that the relation needs to be a 1:1 relation.
And once it said that it had a non nullable field.
I made that nullable int? and then i got the first error again.

I think you need to setup self-referencing relationships manually (specifically, the Horse class properties Sire and Dam are causing an issue).
Try this (in the answer):
What is the syntax for self referencing foreign keys in EF Code First?
You could add two more int IDs representing the foreign keys (SireId, DamId).

If you add this to your model configuration it should work:
modelBuilder.Entity<Horse>()
.HasRequired(h => h.Dam) // or HasOptional
.WithMany();
modelBuilder.Entity<Horse>()
.HasRequired(h => h.Sire) // or HasOptional
.WithMany();
The problem is that the mapping conventions try to create a one-to-one relationship between Dam and Sire and therefore cannot determine what's the principal and what's the dependent because both are optional. Anyway I guess you don't want a one-to-one relationships but actually two one-to-many relationships (the many-side (the "children") not being exposed in the model).

Related

I want to show relationship with following classes but I got these error

public class Newtask
{
[Key]
public int TId { get; set; }
public string Name { get; set; }
public int Estimated_days_of_work { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
public virtual ICollection<Subtask> Subtasks { get; set; }
}
public class Subtask
{
[Key]
public int SId { get; set; }
public string SubName { get; set; }
public int SEstimated_days_of_work { get; set; }
public int NewtaskTId { get; set; }
public virtual Newtask Newtasks { get; set; }
}
public class SubSubtask
{
[Key]
public int SsId { get; set; }
public string SubSubTaskName { get; set; }
public int SsEstimated_days_of_work { get; set; }
public int NewtaskTId { get; set; }
public int SId { get; set; }
public virtual Newtask Newtasks { get; set; }
public virtual Subtask Subtasks { get; set; }
}
Right now I have these classes, I want to show relationship between them like each task have subtask and subtask also have its subtask, but when I run the application I got these error:
System.Data.SqlClient.SqlException: 'Introducing FOREIGN KEY constraint 'FK_dbo.SubSubtasks_dbo.Subtasks_SId' on table 'SubSubtasks' 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.'
How can I modify this relationship?
Your classes are making circular dependency so when you will goto delete some entities it will break up with multiple error. if you ignore on deleting the entities you can add this to your dbcontext class's model builder.
protected override void OnModelCreating(ModelBuilder modelbuilder)
{
foreach (var relationship in modelbuilder.Model.GetEntityTypes().SelectMany(e
=> e.GetForeignKeys()))
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
base.OnModelCreating(modelbuilder);
}
Though the proper solution is to change your business model that will not make a circular dependency.
you can change your classes this way if u don't see any problem:
public class Newtask
{
[Key]
public int TId { get; set; }
public string Name { get; set; }
public int Estimated_days_of_work { get; set; }
public virtual ICollection<Enrollment> Enrollments { get; set; }
public virtual ICollection<Subtask> Subtasks { get; set; }
}
public class Subtask
{
[Key]
public int SId { get; set; }
public string SubName { get; set; }
public int SEstimated_days_of_work { get; set; }
public int NewtaskTId { get; set; }
public virtual Newtask Newtasks { get; set; }
public virtual Substask HasSubtask{get;set;}
}
and change your modelbuilder in dbset to:
modelbuilder.Entity(typeof (Subtask))
.OnDelete(DeleteBehavior.Restrict);

Entity Framework Unable to determine principal end of relationship. Multiple added entities may have same primary key

I'm having some issues with EF.
Migrations go fine, but when I try to run update-database I get the error :
Unable to determine the principal end of the 'LeagueInsight.Models.Image_Passive' relationship. Multiple added entities may have the same primary key.
Here are my models and config:
Passive.cs
public class Passive
{
public long Id { get; set; }
public string Description { get; set; }
public string Name { get; set; }
public string SanitizedDescription { get; set; }
// Navigation
public virtual Image Image { get; set; }
public virtual Champion Champion { get; set; }
}
Image.cs
public class Image
{
public long Id { get; set; }
public string Full { get; set; }
public string Group { get; set; }
public int H { get; set; }
public string Sprite { get; set; }
public int W { get; set; }
public int X { get; set; }
public int Y { get; set; }
// Navigation
public virtual Champion Champion { get; set; }
public virtual ChampionSpell ChampionSpell { get; set; }
public virtual Passive Passive { get; set; }
}
DBContext Configuration Partial:
modelBuilder.Entity<Passive>()
.HasRequired(p => p.Champion)
.WithRequiredPrincipal(c => c.Passive);
modelBuilder.Entity<Info>()
.HasRequired(i => i.Champion)
.WithRequiredPrincipal(c => c.Info);
modelBuilder.Entity<Image>()
.HasOptional(i => i.Champion)
.WithRequired(c => c.Image);
modelBuilder.Entity<Image>()
.HasOptional(i => i.Passive)
.WithRequired(p => p.Image);
base.OnModelCreating(modelBuilder);
It is just supposed to be a 1-1 relationship between the two, an I can't figure out why there would be multiple entities with the same Id here.
Edit: I was asked for the champion class:
public class Champion
{
public int Id { get; set; }
public string Blurb { get; set; }
public string Key { get; set; }
public string Lore { get; set; }
public string Name { get; set; }
public string Partype { get; set; }
public string Title { get; set; }
// Navigation
[InverseProperty("Ally")]
public virtual ICollection<Tip> Allytips { get; set; }
[InverseProperty("Enemy")]
public virtual ICollection<Tip> Enemytips { get; set; }
public virtual Image Image { get; set; }
public virtual Info Info { get; set; }
public virtual Passive Passive { get; set; }
public virtual ICollection<Recommended> Recommended { get; set; }
public virtual ICollection<Skin> Skins { get; set; }
public virtual Stats Stats { get; set; }
public virtual ICollection<ChampionSpell> Spells { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}

WillCascadeOnDelete not working

I have two tables
public class ServiceRendering
{
[DataMember]
public int Id { get; set; }
[DataMember]
public virtual IList<ServiceRenderingProduct> ServiceRenderingProducts { get; set; }
}
public class ServiceRenderingProduct
{
[ForeignKey("ServiceRendering")]
[DataMember]
public int ServiceRenderingId { get; set; }
[DataMember]
public virtual ServiceRendering ServiceRendering { get; set; }
[DataMember]
public int Id { get; set; }
}
And set relation between them
modelBuilder.Entity<ServiceRendering>().HasMany(x => x.ServiceRenderingProducts).WithRequired(x => x.ServiceRendering).HasForeignKey(x => x.ServiceRenderingId ).WillCascadeOnDelete(true);
But on deleting ServiceRenderingEntity, I don't get deleting of ServiceRenderingProduct entities. Should I set any constraints in database? Or is there another reason?

One to many in Entity Framework using fluent mapping

Im trying to do a one-to-many map by using fluent api.
This is my classes
public class Product : EntityBase
{
public Product()
{
this.ProductArticles = new List<ProductArticle>();
}
[Key]
public int ProductId { get; set; }
public string Description { get; set; }
public string ReportText1 { get; set; }
public string ReportText2 { get; set; }
public bool Standard { get; set; }
public int ProductGroupId { get; set; }
public decimal? Surcharge1 { get; set; }
public decimal? Surcharge2 { get; set; }
public decimal? Surcharge3 { get; set; }
public decimal? Surcharge4 { get; set; }
public decimal PriceIn { get; set; }
public decimal PriceOut { get; set; }
public decimal PriceArtisanIn { get; set; }
public decimal PriceArtisanOut { get; set; }
public decimal PriceTotalIn { get; set; }
public decimal PriceTotalOut { get; set; }
public decimal PriceTotalOutVat { get; set; }
public decimal PriceAdjustment { get; set; }
public bool Calculate { get; set; }
public string Notes { get; set; }
[ForeignKey("ProductGroupId")]
public virtual ProductGroup ProductGroup { get; set; }
public virtual ICollection<ProductArticle> ProductArticles { get; set; }
}
public class ProductArticle : EntityBase
{
[Key]
public int ProductArticleId { get; set; }
public int ProductId { get; set; }
public int ArticleId { get; set; }
public decimal Qty { get; set; }
public decimal PriceIn { get; set; }
public bool Primary { get; set; }
public virtual Product Product { get; set; }
public virtual Article Article { get; set; }
}
Now i want from single Product include all ProductArticles
This is my mapping
public class ProductMap : EntityTypeConfiguration<Product>
{
public ProductMap()
{
// Primary Key
this.HasKey(p => p.ProductId);
// Table & Column Mappings
this.ToTable("Product");
this.HasMany(p => p.ProductArticles)
.WithOptional()
.Map(p => p.MapKey("ProductId").ToTable("ProductArticle"));
}
But it doesnt work.. Please help :)
First - by convention EF treats property with name equal to Id or EntityTypeName + Id is a primary key. So, you don't need to configure that manually.
Second - if you don't want table names to be plural, just remove that convention from your context instead of providing table name for each entity mapping:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
And last - EF smart enough to define foreign keys which have names like RelatedEntityTypeName + Id. So, you don't need any fluent configurations here.

Entity Framework Code First - two Foreign Keys from same table

I've just started using EF code first, so I'm a total beginner in this topic.
I wanted to create relations between Teams and Matches:
1 match = 2 teams (home, guest) and result.
I thought it's easy to create such a model, so I started coding:
public class Team
{
[Key]
public int TeamId { get; set;}
public string Name { get; set; }
public virtual ICollection<Match> Matches { get; set; }
}
public class Match
{
[Key]
public int MatchId { get; set; }
[ForeignKey("HomeTeam"), Column(Order = 0)]
public int HomeTeamId { get; set; }
[ForeignKey("GuestTeam"), Column(Order = 1)]
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; }
}
And I get an exception:
The referential relationship will result in a cyclical reference that is not allowed. [ Constraint name = Match_GuestTeam ]
How can I create such a model, with 2 foreign keys to the same table?
Try this:
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);
}
}
Primary keys are mapped by default convention. Team must have two collection of matches. You can't have single collection referenced by two FKs. Match is mapped without cascading delete because it doesn't work in these self referencing many-to-many.
It's also possible to specify the ForeignKey() attribute on the navigation property:
[ForeignKey("HomeTeamID")]
public virtual Team HomeTeam { get; set; }
[ForeignKey("GuestTeamID")]
public virtual Team GuestTeam { get; set; }
That way you don't need to add any code to the OnModelCreate method
I know it's a several years old post and you may solve your problem with above solution. However, i just want to suggest using InverseProperty for someone who still need. At least you don't need to change anything in OnModelCreating.
The below code is un-tested.
public class Team
{
[Key]
public int TeamId { get; set;}
public string Name { get; set; }
[InverseProperty("HomeTeam")]
public virtual ICollection<Match> HomeMatches { get; set; }
[InverseProperty("GuestTeam")]
public virtual ICollection<Match> GuestMatches { get; set; }
}
public class Match
{
[Key]
public int MatchId { 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; }
}
You can read more about InverseProperty on MSDN: https://msdn.microsoft.com/en-us/data/jj591583?f=255&MSPPError=-2147217396#Relationships
You can try this too:
public class Match
{
[Key]
public int MatchId { get; set; }
[ForeignKey("HomeTeam"), Column(Order = 0)]
public int? HomeTeamId { get; set; }
[ForeignKey("GuestTeam"), Column(Order = 1)]
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; }
}
When you make a FK column allow NULLS, you are breaking the cycle. Or we are just cheating the EF schema generator.
In my case, this simple modification solve the problem.
InverseProperty in EF Core makes the solution easy and clean.
InverseProperty
So the desired solution would be:
public class Team
{
[Key]
public int TeamId { get; set;}
public string Name { get; set; }
[InverseProperty(nameof(Match.HomeTeam))]
public ICollection<Match> HomeMatches{ get; set; }
[InverseProperty(nameof(Match.GuestTeam))]
public ICollection<Match> AwayMatches{ get; set; }
}
public class Match
{
[Key]
public int MatchId { get; set; }
[ForeignKey(nameof(HomeTeam)), Column(Order = 0)]
public int HomeTeamId { get; set; }
[ForeignKey(nameof(GuestTeam)), Column(Order = 1)]
public int GuestTeamId { get; set; }
public float HomePoints { get; set; }
public float GuestPoints { get; set; }
public DateTime Date { get; set; }
public Team HomeTeam { get; set; }
public Team GuestTeam { get; set; }
}
This is because Cascade Deletes are enabled by default. The problem is that when you call a delete on the entity, it will delete each of the f-key referenced entities as well. You should not make 'required' values nullable to fix this problem. A better option would be to remove EF Code First's Cascade delete convention:
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
It's probably safer to explicitly indicate when to do a cascade delete for each of the children when mapping/config. the entity.
I know this is pretty old question but coming here in 2021 with EF Core > 3 solution below worked for me.
Make sure to make foreign keys nullable
Specify default behavior on Delete
public class Match
{
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 Team HomeTeam { get; set; }
public Team GuestTeam { get; set; }
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Match>()
.HasRequired(m => m.HomeTeam)
.WithMany(t => t.HomeMatches)
.HasForeignKey(m => m.HomeTeamId)
.OnDelete(DeleteBehavior.ClientSetNull);
modelBuilder.Entity<Match>()
.HasRequired(m => m.GuestTeam)
.WithMany(t => t.AwayMatches)
.HasForeignKey(m => m.GuestTeamId)
.OnDelete(DeleteBehavior.ClientSetNull);
}

Categories