Self-Referencing In Many-To-Many with Cascades - sql-server

So I have a simple many to many relationship like below, and I have manually added onUpdate: ReferantialAction.Cascade on the foreign key part, and when I update my entitiy with a simple SqlCommand I can successfully update my entity.
public class Machine
{
public string No { get; set; }
public string Name { get; set; }
public List<MachinePart> MachineParts { get; set; }
}
public class Part
{
public string No { get; set; }
public string Name { get set; }
public List<MachinePart> MachineParts { get; set; }
}
public class MachinePart
{
public string MachineNo { get; set; }
public Machine Machine { get; set; }
public string PartNo { get; set; }
public Part Part { get; set; }
}
DbContext
builder.Entity<MachinePart>().HasKey(i => new { i.MachineNo, i.PartNo });
Migration
migrationBuilder.AddForeignKey(
name: "FK_MachineParts_Machines_MachineNo",
table: "MachineParts",
column: "MachineNo",
principalTable: "Machines",
principalColumn: "No",
onUpdate: ReferentialAction.Cascade,
onDelete: ReferentialAction.Cascade);
But now I have to cretae a SELF REFERENCING MANY TO MANY TABLE which can be a cascade relation,
public class MachineRelation
{
public string MachineNo { get; set; }
public Machine Machine { get; set; }
public string ReferencedMachineNo { get; set; }
public Machine ReferencedMachine { get; set; }
}
When I manually add
onUpdate: ReferantialAction.Cascade &&
onDelete: ReferantialAction.Cascade
I get cascade exception =>
Introducing FOREIGN KEY constraint 'FK_AssemblyRevisions_Assemblies_RevisionedAssemblyNo' on table 'AssemblyRevisions' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.

Related

How can I solve on delete no action or on update no action problen when using code-first in Entity Framework with authentication

I'm creating a project for hospital automation in user authentication and using code-first in Entity Framework.
Here my Hospital entity:
public int Id { get; set; }
public string Name { get; set; }
public string PhoneNumber { get; set; }
Here my Clinic entity:
public int Id { get; set; }
public string Name { get; set; }
And my HospitalAndClinic entity:
public int Id { get; set; }
public int HospitalId { get; set; }
public int ClinicId { get; set; }
[ForeignKey("HospitalId")]
public Hospital Hospital { get; set; }
[ForeignKey("ClinicId")]
public Clinic Clinic { get; set; }
This is the Doctor entity:
public string Branch { get; set; }
public int? HospitalAndClinicId { get; set; }
[ForeignKey("HospitalAndClinicId")]
public HospitalAndClinic HospitalAndClinic { get; set; }
This is my employee entity
public string Position { get; set; }
public int HospitalAndClinicId { get; set; }
[ForeignKey("HospitalAndClinicId")]
public HospitalAndClinic HospitalAndClinic { get; set; }
My Doctorand 'Employee' tables extend from Person class that has fields like id, name etc.
When I do migration I get this problem
Introducing FOREIGN KEY constraint 'FK_Doctor_HospitalAndClinic_HospitalAndClinicId' on table 'Doctor' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
How can I solve this?
In the migration, under constrains you can add onDelete property to say what happens when deleted.
migrationBuilder.CreateTable(
name: "Doctor",
columns: table => new
{
Id = table.Column<int>(nullable: false),
.
.
},
constraints: table =>
{
table.PrimaryKey("PK_Doctor", x => x.Id);
table.ForeignKey(
name: "FK_Doctor_HospitalAndClinic_HospitalAndClinicId",
column: x => x.HospitalAndClinicId,
principalTable: "HospitalAndClinic",
principalColumn: "Id",
onDelete: ReferentialAction.NoAction); // <---- Add this.
});
Or what you can also do is, as Hopeless pointed out, use fluent API to configure your model by overriding the OnModelCreating method in your derived context.
modelBuilder.Entity<Doctor>()
.HasOne(e => e.HospitalAndClinic)
.WithMany()
.OnDelete(DeleteBehavior.NoAction); // <-- Add this
Visit here to see all DeleteBehaviors and visit here to see all ReferentialActions

The INSERT statement conflicted with the FOREIGN KEY. Entity includes ForeignKey Id property

I'm doing one to many relationship database with Entity Framework with an Id property.
I have two model classes:
public class PersonModel
{
[Key]
public int PersonId { get; set; }
public string NickName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int TeamRefId { get; set; }
[ForeignKey("TeamRefId")]
public virtual TeamModel TeamModel { get; set; }
}
public class TeamModel
{
public TeamModel()
{
TeamMembers = new List<PersonModel>();
this.Tournaments = new HashSet<TournamentModel>();
}
[Key]
public int TeamId { get; set; }
public string TeamName { get; set; }
public virtual ICollection<PersonModel> TeamMembers { get; set; }
public virtual ICollection<TournamentModel> Tournaments { get; set; }
public virtual MatchUpEntryModel MatchupEntry { get; set; }
public virtual MatchUpModel Matchup { get; set; }
}
When I'm trying to create a new Person entity, I get this error:
SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.PersonModel_dbo.TeamModel_TeamRefId". The conflict occurred in database "Tournament2", table "dbo.TeamModel", column 'TeamId'.
Making Foreign Key in Person Model nullable should solve your problem
note that created person will have No Team until you Modify it later after your create Team
public class PersonModel
{
[Key]
public int PersonId { get; set; }
public string NickName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public int? TeamRefId { get; set; }
[ForeignKey("TeamRefId")]
public virtual TeamModel TeamModel { get; set; }
}

Entity Framework Model Mapping With Legacy Database and Constant

I'm using EF6 over a DB that's over 15 years old. I did not make this architecture decision. All of my experience with EF has been code-first, with models I've created myself.
One of our tables has a reference table that has some info needed in selects only. These mappings will never be used for update/insert/delete.
I have two entities. My primary table:
public class QualParam
{
[Key]
public string MillId { get; set; }
[Key]
public string Qparam { get; set; }
public string ValueNum { get; set; }
public string ActiveFlag { get; set; }
public int ModifiedTimestamp { get; set; }
public int CreatedTimestamp { get; set; }
public decimal SbIncrement { get; set; }
public string QualityDesc { get; set; }
public string TypeCode { get; set; }
public QualParamHeader QualParamHeader { get; set; }
public virtual UnitMeasure UnitMeasure { get; set; }
}
and a reference table:
public class UnitMeasure
{
[Key]
public string UnitOfMeasure { get; set; }
public int ModifiedTimestamp { get; set; }
public int CreatedTimestamp { get; set; }
public string BaseUnits { get; set; }
public string UnitDesc { get; set; }
[Key]
public string TableName { get; set; }
public string RollWeightFlag { get; set; }
public string MetricFlag { get; set; }
public string MxActionCode { get; set; }
[Key]
public string TypeCode { get; set; }
public byte[] RecordVersion { get; set; }
public List<QualParam> QualParams { get; set; }
}
QualParam may have a UnitMeasure, and UnitMeasure can have many QualParams, easy, right?
In SQL the join is done thusly
SELECT *
FROM qual_params AS q
LEFT JOIN unit_measure AS u
ON u.unit_meas = q.unit_meas
AND u.table_name = 'qual_params'
AND u.type_code = q.type_code
So yes, there's a constant, the table name, and yes the keys from the source to the reference tables don't match, and aren't even enumerated in the db to begin with. Again, legacy.
Our Db context-
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new QualParamsConfiguration());
modelBuilder.Configurations.Add(new UnitMeasureConfiguration());
base.OnModelCreating(modelBuilder);
}
I'm using auto generated configs-
public QualParamConfiguration()
: this("dbo")
{
}
public QualParamConfiguration(string schema)
{
ToTable(schema + ".qual_params");
HasKey(x => new { x.MillId, x.Qparam });
Property(x => x.Qparam).HasColumnName(#"qparam").IsRequired().IsFixedLength().IsUnicode(false).HasColumnType("char").HasMaxLength(10).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
Property(x => x.ValueNum).HasColumnName(#"value_num").IsRequired().IsFixedLength().IsUnicode(false).HasColumnType("char").HasMaxLength(9);
Property(x => x.ActiveFlag).HasColumnName(#"active_flag").IsRequired().IsFixedLength().IsUnicode(false).HasColumnType("char").HasMaxLength(1);
Property(x => x.ModifiedTimestamp).HasColumnName(#"ts_modified").IsRequired().HasColumnType("int");
Property(x => x.CreatedTimestamp).HasColumnName(#"ts_create").IsRequired().HasColumnType("int");
Property(x => x.SbIncrement).HasColumnName(#"sb_increment").IsRequired().HasColumnType("decimal").HasPrecision(7, 3);
Property(x => x.QualityDesc).HasColumnName(#"quality_desc").IsRequired().IsFixedLength().IsUnicode(false).HasColumnType("char").HasMaxLength(50);
Property(x => x.MillId).HasColumnName(#"mill_id").IsRequired().IsFixedLength().IsUnicode(false).HasColumnType("char").HasMaxLength(10).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
this.HasRequired(a => a.QparamHeader).WithMany(b => b.QualParams).HasForeignKey(c => c.Qparam).WillCascadeOnDelete(false);
}
The last navigation wrote itself, for obvious reasons. Simple relationship.
This one, not so much. I'm open to everything- I'm fairly aware at this point that I'll have to create a separate model to achieve this.
The examples I've seen using .Map must be from older versions of EF, since many of the methods are not available to me. Is it even possible to achieve this relationship?

Create multiple foreign keys referes to the same table in EF code first

In my table it has two foreign keys which refers to the same table. When I do the migration(Entity Framework Code first Approach) it pops up error as,
"One or more validation errors were detected during model generation:
Dog_Sire_Target: : Multiplicity is not valid in Role 'Dog_Sire_Target' in relationship 'Dog_Sire'. Because the Dependent Role properties are not the key properties, the upper bound of the multiplicity of the Dependent Role must be '*'.".
But if I add only one foreign key it works properly. Here is my table structure.
public class Dog
{
[Key]
public int Dog_Id { get; set; }
public string Dog_Name { get; set; }
[ForeignKey("Sire")]
public int? Dog_SireId { get; set; }
[ForeignKey("Dam")]
public int? Dog_DamId { get; set; }
[ForeignKey("Dog_SireId")]
public virtual Dog Sire { get; set; }
[ForeignKey("Dog_DamId")]
public virtual Dog Dam { get; set; }
}
Try this model:
public class Dog
{
[Key]
public int DogID { get; set; }
public string DogName { get; set; }
public int? DogSireID { get; set; }
[ForeignKey("DogSireID")]
public virtual Dog DogSire { get; set; }
public int? DogDamID { get; set; }
[ForeignKey("DogDamID")]
public virtual Dog DogDam { get; set; }
[InverseProperty("DogSire")]
public virtual ICollection<Dog> DogsSires { get; set; }
[InverseProperty("DogDam")]
public virtual ICollection<Dog> DogsDams { get; set; }
}

Entity Framework 6 foreign key

I am coding an MVC5 internet application with EF6, and have a question in regards to a foreign key name.
I have a model called MapLocationList that has these two fields:
public int mapLocationListGalleryId { get; set; }
public virtual MapLocationListGallery mapLocationListGallery { get; set; }
When EF creates the table, there is both the following columns:
mapLocationListGalleryId
MapLocationListGallery_Id
Can someone please explain why there are two columns for the MapLocationListGallery foreign key?
Thanks in advance
EDIT
I have changed the name to use an uppercase M, yet the additional column is still there.
Here is my model:
public class MapLocationList : IMapLocationItemWithAssets
{
[Key]
public int Id { get; set; }
[Required]
public string name { get; set; }
public bool enabled { get; set; }
[ScaffoldColumn(false)]
public string mapLocationItemType { get; set; }
[ScaffoldColumn(false)]
public string userName { get; set; }
[ScaffoldColumn(false)]
public DateTime creationDate { get; set; }
[ScaffoldColumn(false)]
public DateTime lastUpdate { get; set; }
public string thumbnailDisplayText { get; set; }
public bool parentIsMapLocation { get; set; }
public int thumbnailAssetId { get; set; }
public virtual Asset thumbnailAsset { get; set; }
public int mapLocationId { get; set; }
public virtual MapLocation mapLocation { get; set; }
public int mapLocationListGalleryId { get; set; }
public virtual MapLocationListGallery mapLocationListGallery { get; set; }
public virtual ICollection<MapLocationListItem> listItems { get; set; }
public MapLocationList()
{
creationDate = DateTime.Now;
lastUpdate = DateTime.Now;
listItems = new List<MapLocationListItem>();
}
}
I also have the following in the OnModelCreating function:
modelBuilder.Entity<MapLocationListGallery>()
.HasRequired(c => c.thumbnailAsset)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<MapLocationList>()
.HasRequired(c => c.thumbnailAsset)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<MapLocationList>()
.HasRequired(c => c.mapLocationListGallery)
.WithMany()
.WillCascadeOnDelete(false);
modelBuilder.Entity<MapLocationListItem>()
.HasRequired(c => c.thumbnailAsset)
.WithMany()
.WillCascadeOnDelete(false);
I use this approach as well and I do not experience this behavior. Probably you need to rename your properties to CamelCase (note the capital M):
public int MapLocationListGalleryId { get; set; }
public virtual MapLocationListGallery MapLocationListGallery { get; set; }
If that doesn't help take a look at the ForeignKeyAttribute here and here.
Edit
I'm not familiar with the fluent api, but I think you could try to set the foreign key explicitly using something like:
modelBuilder.Entity<MapLocationList>()
.HasRequired(c => c.mapLocationListGallery)
.WithMany()
.HasForeignKey(x => x.mapLocationListGalleryId)
.WillCascadeOnDelete(false);
For more info see this article, topic: "Configuring Unconventional Foreign Key Names". Although it's strange this is necessary because your code seems to comply with the Code First convention (with capital M, i.e. the class name).

Resources