I have the following Entity Framework 5 code first classes
public class Airplane
{
public int Id { get; set; }
public int LeftWingId { get; set; }
public virtual Wing LeftWing { get; set; }
public int RightWingId { get; set; }
public virtual Wing RightWing { get; set; }
}
public class Wing
{
public int Id { get; set; }
}
Airplane has one left and one right wing (both are required). Wing may belong to a 0..1 airplane (as a left or right wing) or to some other "flying device".
Deleting an airplane should cascade-delete it's wings.
How can this be configured in code-first fluent API?
Is it possible to have two 0..1 --- 1 associations in EF with cascade delete on both?
Unfortunately, you cannot have two associations pointing from same source table to same target table with cascade delete on both.
However, that is not due to limitation in EF, but in SQL server.
Quote from answer on another question you might want to check:
SQL Server does simple counting of cascade paths and, rather than
trying to work out whether any cycles actually exist, it assumes the
worst and refuses to create the referential actions (CASCADE): you can
and should still create the constraints without the referential
actions. If you can't alter your design (or doing so would compromise
things) then you should consider using triggers as a last resort.
Related
I have a movie entity and a actor entity, these 2 entities have a many to many relationship, so I've mapped it as ManyToMany(x=>x.Movies) and ManyToMany(x=>x.Actors) but i'd like to have the character the actor played on the movie, it should stay on the MoviesActorsPivot as a new column
But how can I do that using the Fluent Nhibernate Mapping in a way that I could get and save the data as easy as nhibernate does?
Not creating the pivot table manually and making the HasMany(x => x.MoviesActorsPivot) on both sides and managing the association by my own.
Edit:
Or if I map it creating the HasMany(x => x.MoviesActorsPivot) on both sides, how would i manage to insert and get al data like all movies from an actor or all actors that act on a movie, getting all the characters names?
The answer is:
NHibernate native many-to-many mapping does not support any additional setting on the pairing table
But, it could be replaced with a pairing object being first level citizen
public class MovieActor
{
public virtual Movie Movie { get; set; }
public virtual Actor Actor { get; set; }
... // more properties here
public virtual int Rating { get; set; }
}
public class Actor
{
public virtual IList<MovieActor> Movies { get; set; }
}
public class Movie
{
public virtual IList<MovieActor> Actors { get; set; }
}
That would be standard HasMany and References mapping. And the queriyng later will be more easier
Also check these:
Nhibernate: How to represent Many-To-Many relationships with One-to-Many relationships?
nhibernate many to many with multiple table
many-to-many with extra columns nhibernate
The problem is i cannot perform cascade deletion using only EF codefirst conventions. They, in particular, say: "If a foreign key on the dependent entity is not nullable, then Code First sets cascade delete on the relationship"
I have parent and child entities:
[Table("AssociationPages")]
public class AssociationPage
{
[Column("AssociationPageID"), Required, Key]
public int Id { get; set; }
[Required, ForeignKey("AssociationSetting")]
public int AssociationId { get; set; }
public virtual AssociationSetting AssociationSetting { get; set; }
}
[Table("AssociationSetting")]
public class AssociationSetting
{
[Required, Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AssociationId { get; set; }
public virtual ICollection<AssociationPage> Pages { get; set; }
}
My AssociationPages table in MS SQL Server looks like:
CREATE TABLE [dbo].[AssociationPages](
[AssociationPageID] [int] IDENTITY(1,1) NOT NULL,
[AssociationId] [int] NOT NULL,
...
)
and a FK (but it shouldnt matter as EF has its own conventions):
ALTER TABLE [dbo].[AssociationPages] WITH CHECK ADD CONSTRAINT [FK_ChamberPages_Chambers] FOREIGN KEY([AssociationId])
REFERENCES [dbo].[AssociationSetting] ([AssociationId])
GO
So i have non-nullable FK everywhere but once i try to delete parent AssociationSetting row then getting the "The DELETE statement conflicted with the REFERENCE constraint FK_ChamberPages_Chambers. The conflict occurred in database ..., table dbo.AssociationPages, column AssociationId message". I know i can set constraints inside database or with EF fluent API but why this is not working?
Thanks for your ideas!
update
WillCascadeOnDelete() doesnt work as well :(
It may be that Code First is not setting up the cascade since you are not following the naming conventions.
Try this:
public class AssociationPage
{
public int Id { get; set; }
public int AssociationSettingId { get; set; }
public virtual AssociationSetting AssociationSetting { get; set; }
}
public class AssociationSetting
{
public int Id { get; set; }
public virtual ICollection<AssociationPage> Pages { get; set; }
}
Okay, in investigation purposes i created a simpliest parent-child tables, put two rows - one per table, created FK relationship as "No Action" on cascade delete and wrote EF Code First entities.
Then I set up FK relationship variuos ways - via column attributes, Fluent API, explicitly specifying WillDeleteOnCascade() method or alltogether but had no luck trying to delete parent row. The only way I achieved this when retrieved both parent and child records prior to removing. At this point SQL profiler shown that rows being deleted one by one both for parent and children tables.
Summarizing the above I suppose the cascading in EF Code First means the setting constraints on the database being created by EF. I might be missing something thu.
I regularly have the following structure:
MyClass
public virtual ICollection<Version> Versions { get; set; }
public virtual Version CurrentVersion { get; set; }
That is, there is a list of stuff, and some class both points to that list, and one specific item in that list - either the current version of many versions, the next upcoming event in a list of events, etc.
In my schema what I'd like to end up with is a Foreign Key pointing from Version to MyClass - that much works out properly. But then I'd like a Foreign Key pointing from MyClass to Version representing the CurrentVersion property, with no Foreign Key pointing back - I don't want the extra storage or bother of telling a Version what MyClass it's the CurrentVersion for, if any. Put another way, I'd like this second relationship to be one-way from MyClass to Version, even though it's one-to-one.
What EF Code First gives me instead is the normal one-to-many on the first property, with the FK from Version to MyClass, but then a full one-to-one relationship on the second property with an FK pointing in both directions - so the underlying schema for Version ends up with MyClass_Id and MyClass_Id1.
So, is there a way to get a one-way relationship in EF Code First without resorting to the Fluent API? It looked like maybe System.ComponentModel.DataAnnotations.Schema.InverseProperty had a shot at it, but it didn't seem to offer a way to say "Don't generate one."
The key is to specify the InverseProperty on the property that points back, so that EF realizes it's to the Many-to-Many, not to the One-to-One.
public class MyClass
{
public int Id { get; set; }
public Version CurrentVersion { get; set; }
public ICollection<Version> Versions { get; set; }
}
public class Version
{
public int Id { get; set; }
[InverseProperty("Versions")]
public Versioned Versioned { get; set; }
}
A developer can be affected to 0 or more task. A task needs at least one developer to be executed. How do I express that in Code first?
public class Task
{
public int ID
//more properties here
public virtual ICollection<Developer> Developers { get; set; }
}
public class Developer
{
public int ID
//more properties here
public virtual ICollection<Task> Tasks { get; set; }
}
Thanks for helping
It's still a regular many-to-many relationship, and it should get mapped correctly by default. Specific business (validation) rules do not change its nature.
You can implement IValidatableObject if you want to enforce specific cardinalities when saving.
Part of my project is to persist data from another source. In this case we have an SAP data source that we will need to pull data from. I need to take the data from SAP and map it to entities I have in my application. Here is an example of an entity I have in my application:
public class Project : BaseEntity
{
public string Name { get; set; }
public string ProjectNumber { get; set; }
public string Description { get; set; }
public string CreatedBy { get; set; }
public string ModifiedBy { get; set; }
public string Currency { get; set; }
#region Navigation Properties
public virtual Address Address { get; set; }
public virtual CompanyCode CompanyCode { get; set; }
public virtual ICollection<Contact> TeamMembers { get; set; }
#endregion
}
As you can see, I have child objects that I map from SAP as well. I need some advice on the best way to insert and update my entities. I am struggling with knowing when to add (insert) entities to my context and when to attach (update) them, because SAP doesn't have knowledge of what my application may or may not have. I need to guard against duplicates, too. For example, should I perform a lookup of each child entity in my parent entity to see if they exist before I apply them to the parent? Then, add / attach the entire parent object to the context or handle each entity separately while still maintaing their relationships?
Yes you must manually test everything to make correct decision what must be inserted, updated or deleted. Depending on the application you can use some more complex queries to reduce number of round trips to the database - for example you can use single query with Contains to load all TeamMembers needed for processed Project or you can load Project with including all related data if you also need to test if project exists.
I did large synchronization application before and I end up with pre-loading all entities at the beginning with few queries and working completely in memory.
Don't forget to use DbSet's Local property or Find method to take advantage of already loaded entities.
You can also use some custom stored procedures to improve performance of this operation.