EF6 many-to-many, WillCascadeOnDelete and multiple cascade paths - c#

In EF6 CodeFirst, I want to model a many-to-many relationship with navigation properties.
The classes:
public class Event
{
[DataMember]
[Required]
public int Id { get; set; }
// Other properties
[DataMember]
public virtual ICollection<Notification> Notifications { get; set; }
}
public class Notification
{
[DataMember]
[Required]
public int Id { get; set; }
// Other properties
[DataMember]
public virtual ICollection<Event> Events { get; set; }
}
class EventConfiguration : EntityTypeConfiguration<Event>
{
public EventConfiguration()
{
HasMany(e => e.Notifications)
.WithMany(e => e.Events)
.Map(m => m.ToTable("NotificationEvents"));
}
}
There are cascade deletes on Event and Notification from other tables.
If I try to create the database, I get:
Introducing FOREIGN KEY constraint
'FK_dbo.NotificationEvents_dbo.Events_Event_Id' on table
'NotificationEvents' may cause cycles or multiple cascade paths.
Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other
FOREIGN KEY constraints.
Which is fair enough, given other cascades. Now my question is: how to (or can I) specify no cascades on one or both sides of this relationship only?
If I add (as suggested in another post):
class NotificationConfiguration : EntityTypeConfiguration<Notification>
{
public NotificationConfiguration()
{
HasOptional(e => e.Events).WithMany().WillCascadeOnDelete(false);
}
}
I get:
The navigation property 'Events' declared on type
'Notification' has been configured with
conflicting multiplicities.

Related

entities that do not expose foreign key properties for their relationships (EF) [duplicate]

This question already has answers here:
An error occurred while saving entities that do not expose foreign key properties for their relationships
(16 answers)
Closed 2 months ago.
When trying to call SaveChanges(), I get the following error:
An error occurred while saving entities that do not expose foreign key properties for their relationships. The EntityEntries property will return null because a single entity cannot be identified as the source of the exception. Handling of exceptions while saving can be made easier by exposing foreign key properties in your entity types. See the InnerException for details.'
SqlException: Invalid column name 'Artwork_Id'
I am using Entity Framework.
I'm trying to add an artworkImage that has the Id of an artwork as a reference. All information is being passed correctly but it's not saving.
I've tried adding foreign keys to my models and dbcontext but I've not gotten further than the code below.
public partial class ArtworkImage
{
[Key]
public int Id { get; set; }
public string ImageURL { get; set; }
public Artwork Artwork { get; set; }
}
public partial class Artwork
{
[Key]
public int Id { get; set; }
public string Category { get; set; }
public ICollection<ArtworkImage> ArtworkImage { get; set; }
}
My DbContext:
public DbContext()
: base("name=DbConnection")
{
this.Configuration.AutoDetectChangesEnabled = false;
}
public virtual DbSet<Artwork> Artworks { get; set; }
public virtual DbSet<ArtworkImage> ArtworkImages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Artwork>()
.Property(e => e.Category)
.IsFixedLength();
modelBuilder.Entity<Artwork>()
.HasKey(b => b.Id);
modelBuilder.Entity<ArtworkImage>()
.HasKey(b => b.Id);
Database.SetInitializer<DbContext>(null);
base.OnModelCreating(modelBuilder);
}
I believe I should be adding something like this to my dbcontext but I haven't quite figured it out yet.
modelBuilder.Entity<ArtworkImage>()
.HasRequired(p => p.Artwork)
.WithMany(d => d.ArtworkImage)
.HasForeignKey(p => p.Artwork);
If any information is missing please point it out and I'll add it.
You have to declare primary key on each table. it is a rare occasion when a table has no PK. almost never.
[Key]
public int Id { get; set; }
So part of your problem might be that you don't define the relationship in reverse which I believe is important in how it establishes if the relationship is one-to-one or one-to-many. So you will likely need to add a property on the Artwork class that is of type ArtworkImage (if it is one-to-one). if it is one-to-many you will need to make the property some generic collection with the generic of type ArtworkImage.
One-to-one
public partial class Artwork
{
[Key]
public int Id { get; set; }
public string Category { get; set; }
public ArtworkImage ArtworkImage { get; set; }
}
One-to-many
public partial class Artwork
{
[Key]
public int Id { get; set; }
public string Category { get; set; }
public IEnumerable<ArtworkImage> ArtworkImages { get; set; }
}

Cascade delete of a parent entity with two children to the same table

I have two models:
Child
public class Child {
public int Id { get; set; }
public string Name { get; set; }
}
Parent
public class Parent {
public int Id { get; set; }
public int? FirstChildId { get; set; }
public Child FirstChild { get; set; }
public int? SecondChildId { get; set; }
public Child SecondChild { get; set; }
}
There is a strict relationship that one child has only one parent and one parent has zero-or-one FirstChild and zero-or-one SecondChild.
As far as I know, if we want to make a relationship to the same table (model) twice then we need to have a one-to-many relationship instead of one-to-one.
Therefore, I've modified the Child class by adding Many relationships.
public class Child {
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Parent> ParentsFirstChild { get; set; }
public ICollection<Parent> ParentsSecondChild { get; set; }
}
So far I have the next Fluent API modelBuilder that works
modelBuilder.Entity<Child>()
.HasMany(f => f.ParentsFirstChild)
.WithOptional(p => p.FirstChild)
.HasForeignKey(p => p.FirstChildId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Child>()
.HasMany(f => f.ParentsSecondChild)
.WithOptional(p => p.SecondChild)
.HasForeignKey(p => p.SecondChildId)
.WillCascadeOnDelete(false);
The problem is that if we'd put
.WillCascadeOnDelete(true)
It produces the error
Introducing FOREIGN KEY constraint
FK_dbo.Parent_dbo.Child_ParentsFirstChildId on table Parent may cause
cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON
UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
I see that EF thinks that a Child could be related to other Parents. That's why it could be cyclic, right?
Question
How to create a mapping through either DataAnnotations or Fluent API, so that in case of presence (both optional) one or both children (same class/model) during deleting of a parent, children will be removed too?

Multiplicity is not valid in Role in relationship: EF code first one to one relationship with same primary key and foreign key

I have two entities with one to one relationship where in target entity primary key and foreign key are same. Here is two entity and their fluent mapping.
public class Register
{
public int Id { get; set; }
public string Number { get; set; }
// N.B: Here I can't have this virtual property for my project dependencies.
//public virtual CustomerDisplay CustomerDisplay { get; set; }
}
public class CustomerDisplay
{
public int RegisterId { get; set; }
public double ReceiptViewPercent { get; set; }
public virtual Register Register { get; set; }
}
public class RegisterConfiguration : EntityConfig<Register>
{
public RegisterConfiguration(bool useIdentity, bool sqlServerCe)
: base(sqlServerCe)
{
this.HasKey(t => t.Id);
if (!useIdentity)
{
Property(d => d.Id).IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
}
this.Property(t => t.Number).IsRequired().HasMaxLength(20);
}
}
public class CustomerDisplayConfiguration: EntityConfig<CustomerDisplay>
{
public CustomerDisplayConfiguration(bool sqlServerCe)
: base(sqlServerCe)
{
this.HasKey(t => t.RegisterId);
this.HasRequired(t => t.Register).WithMany().HasForeignKey(d => d.RegisterId).WillCascadeOnDelete(false);
}
}
I am getting following error:
I have seen lots of related questions in stackoverflow but didn't find my solution. This one best match with my issue:
How to declare one to one relationship ...
Can anyone tell me how can I get rid of this issue. Thanks
Add again the CustomerDisplay navigation property:
public class Register
{
public int Id { get; set; }
public string Number { get; set; }
public virtual CustomerDisplay CustomerDisplay { get; set; }
}
And configure the relationship as I show as follow:
this.HasRequired(t => t.Register).WithOptional(r=>r.CustomerDisplay);
Notice that you didn’t need to use HasForeignKey to specify that CustomerDisplay.CustomerId is the FK. This is because of Entity Framework’s requirement that the primary key of the dependent be used as the foreign key. Since there is no choice, Code First will just infer this for you.
Update
If you can't add the CustomerDisplay navigation property into Register class, then I suggest you create an unidirectional one to one relationship. Use this configuration:
this.HasRequired(t => t.Register);
That is enough to tell EF who is the principal and who is the dependent entity in your relationship.

EF6 code first multiple 1-to-many mapping issue / "Multiplicity" error

I am receiving the following error when attempting to create the database:
One or more validation errors were detected during model generation:
Interaction_CauseElement_Source: : Multiplicity is not valid in Role
'Interaction_CauseElement_Source' in relationship
'Interaction_CauseElement'. Because the Dependent Role properties are
not the key properties, the upper bound of the multiplicity of the
Dependent Role must be '*'.
Interaction_EffectElement_Source: : Multiplicity is not valid in Role
'Interaction_EffectElement_Source' in relationship
'Interaction_EffectElement'. Because the Dependent Role properties are
not the key properties, the upper bound of the multiplicity of the
Dependent Role must be '*'.
I've seen this error in other Stack Overflow posts, but in the examples I found, the OP was trying for a 1-to-1 relationship in both directions between the tables. That is not what I am looking for.
Here is my model:
public class Element
{
[Key]
public int ID { get; set; }
[Required, MaxLength(64)]
public string Name { get; set; }
[MaxLength(200)]
public string Description { get; set; }
}
public class Interaction
{
[Key]
public int ID { get; set; }
[Index, Required]
public int CauseID { get; set; }
[Index, Required]
public int EffectID { get; set; }
[MaxLength(64)]
public string Location { get; set; }
[ForeignKey("CauseID")]
public virtual Element CauseElement { get; set; }
[ForeignKey("EffectID")]
public virtual Element EffectElement { get; set; }
}
Items in the Elements table are unique. A pair of elements can interact with each other in any number of locations. The CauseID/EffectID pair is not going to be unique.
The only other place I am changing the model is in the OnModelCreating method. I had received this error:
Introducing FOREIGN KEY constraint
'FK_dbo.Interactions_dbo.Elements_Cause' on table
'Interactions' 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.
And had to create a cascade policy for the model. This code fixed that error:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Prevent cyclic cascade on elements table
modelBuilder.Entity<Interaction>()
.HasRequired(i => i.CauseElement)
.WithRequiredDependent()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Interaction>()
.HasRequired(i => i.EffectElement)
.WithRequiredDependent()
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
But then I received the cryptic "Multiplicity" error. It seems like it wants me to make public virtual Element CauseElement into a collection like public virtual ICollection<Element> CauseElement, but that would not properly model the relationship.
I found the solution. This article on EntityFrameworkTutoral.net helped out. Because I need TWO references from the Interaction class to the Element class, this relationship is too complex to model in EF with only the attributes.
I had to update the model and then use the fluent API to tell EF how to treat the relationships. I updated my model to the following:
public class Element
{
public Element()
{
CauseElements = new List<Interaction>();
EffectElements = new List<Interaction>();
}
[Key]
public int ID { get; set; }
[Required, MaxLength(64)]
public string Name { get; set; }
#region Navigation
public virtual ICollection<Interaction> CauseElements { get; set; }
public virtual ICollection<Interaction> EffectElements { get; set; }
#endregion
}
public class Interaction
{
[Key]
public int ID { get; set; }
[Index]
public int CauseID { get; set; }
[Index]
public int EffectID { get; set; }
[MaxLength(64)]
public string Location { get; set; }
#region Navigation
[ForeignKey("CauseID")]
public virtual Element CauseElement { get; set; }
[ForeignKey("EffectID")]
public virtual Element EffectElement { get; set; }
#endregion
}
And in my DbContext class I used the fluent API to create the link between the Interaction.CauseElement and Element.CauseElements and which property was the foreign key for the Interaction table (and the same with the Effect relationship):
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Prevent cyclic cascade on elements table
modelBuilder.Entity<Interaction>()
.HasRequired(i => i.CauseElement)
.WithRequiredDependent()
.WillCascadeOnDelete(false);
modelBuilder.Entity<Interaction>()
.HasRequired(i => i.EffectElement)
.WithRequiredDependent()
.WillCascadeOnDelete(false);
//Create the links between the element, the key, and the collection
modelBuilder.Entity<Interaction>()
.HasRequired<Element>(i => i.CauseElement)
.WithMany(e => e.CauseElements)
.HasForeignKey(i => i.CauseID);
modelBuilder.Entity<Interaction>()
.HasRequired<Element>(i => i.EffectElement)
.WithMany(e => e.EffectElements)
.HasForeignKey(i => i.EffectID);
base.OnModelCreating(modelBuilder);
}
It seems that Entity Framework tries to automatically infer the relationships between the tables when you have a simple 1-to-many relationship. If I removed EffectElement from the Interaction class (and EffectElements from Element), EF was able to create the relationship easily. But when I added it back, I received the error again.
Since that Element type showed up twice in the Interaction class, it didn't know how to create the relationship. I had to explicitly define it in the OnModelCreating method.
You reversed the responsibilities of the "ForeignKey" attribute. It goes on the ID field, specifying the property for which it serves as the foreign key. You want something as below:
// To-One on Element
[ForeignKey("Element")]
public int ElementId { get; set; }
public virtual Element Element { get; set; }
Also, this is actually a one-to-one relationship. A one-to-many relationship in this case would be:
// To-Many on Element
public virtual ICollection<Element> Elements{ get; set; }

Entity has two properties which both reference the same entity type in one-to-many relationship

This seems like the most common relationship but for some reason I cannot get code-first EF working. When I run the code below I get the following error:
*{"Introducing FOREIGN KEY constraint 'Recording_RecordingLocation' on table 'Recordings' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.\r\nCould not create constraint. See previous errors."}*
I have researched SO and other places but have not been able to figure this out. I must be having a minor stroke so if this is duplicative I apologize. I don't think it is because all other reference questions I found were for many-to-many relationships... many-to-one.
My scenario is quite simple...
I have an entity (Recording) that has two required properties RecordingLocation and EditingLocation which are both of the same type WorkLocation. Each Recording has exactly one RecordingLocation and one EditingLocation (not many-to-many). I also have the requisite navigation properties.
Each WorkLocation is stand-alone and is not intrinsically linked to the Recording -- it's just a physical place where some work on that Recording took place. So when I delete a recording I do not want to delete the associated WorkLocations.
public class Recording
{
[Key]
public virtual int Id { get; set; }
//... other properties not shown here
public virtual int RecordingLocationId { get; set; }
public virtual WorkLocation RecordingLocation { get; set; }
public virtual int EditingLocationId { get; set; }
public virtual WorkLocation EditingLocation { get; set; }
{
public class WorkLocation
{
[Key]
public virtual int Id { get; set; }
public virtual WorkLocationType Type { get; set; }
public virtual string Description { get; set; }
public virtual LogicalStatus Status { get; set; }
}
// I'll use this on the front-end to filter a selection list
// but don't necessarily assume a Work Location is bound to only items of this type
public enum WorkLocationType
{
RecordingLocation,
EditingLocation,
MasteringLocation
}
What am I missing to get this working?
Your navigation properties RecordingLocation and EditingLocation are required because the corresponding foreign key properties are not nullable. By convention EF assumes that cascading delete is active for a required one-to-many relationship which causes a problem if you have more than one such relationship refering to the same table - hence the exception.
You must disable cascading delete (also your business logic seems to require it) which is only possible in Fluent API:
public class MyContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Recording>()
.HasRequired(r => r.RecordingLocation)
.WithMany()
.HasForeignKey(f => f.RecordingLocationId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Recording>()
.HasRequired(r => r.EditingLocation)
.WithMany()
.HasForeignKey(f => f.EditingLocationId)
.WillCascadeOnDelete(false);
}
}

Categories