EF One-to-many Foreign Keys without child navigation properties - c#

Using code-first Entity Framework and .NET 4, I'm trying to create a one-to-many relationship between parents to children:
public class Parent
{
[Key]
public int ParentId { get; set; }
[Required]
public string ParentName { get; set; }
public IEnumerable<Child> Children { get; set; }
}
public class Child
{
[Key]
public int ChildId { get; set; }
[ForeignKey]
public int ParentId { get; set; }
[Required]
public string ChildName { get; set; }
}
As pointed out here, in order for foreign key relationship to carry into the database, the actual objects must be linked, not just their IDs. The normal way to do this if for a child to contain a reference to its parent (example).
But how do I enforce foreign keys in my implementation, which is the other way around (parent referencing children)?

First of all: You cannot use IEnumerable<T> for a collection navigation property. EF will just ignore this property. Use ICollection<T> instead.
When you have changed this, in your particular example you don't need to do anything because the foreign key property name follows the convention (name of primary key ParentId in principal entity Parent) so that EF will detect a required one-to-many relationship between Parent and Child automatically.
If you had another "unconventional" FK property name you still could define such a mapping with Fluent API, for example:
public class Child
{
[Key]
public int ChildId { get; set; }
public int SomeOtherId { get; set; }
[Required]
public string ChildName { get; set; }
}
Mapping:
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithRequired()
.HasForeignKey(c => c.SomeOtherId);
As far as I can tell it is not possible to define this relationship with data annotations. Usage of the [ForeignKey] attribute requires a navigation property in the dependent entity where the foreign key property is in.

Related

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?

EF Core Foreign Key via Fluent API

I have a 2 classes:
public class Item
{
public int Id { get; set; }
public string ItemName { get; set; }
}
public class ItemStats //inhenrit from Item
{
public int Id { get; set; }
public int MaxEnhanceLevel { get; set; }
public Item Item { get; set; }
}
This is a TPT but since it's not supported out of the box I can't use inheritance. I know how to achieve this using data annotation
[ForeignKey(nameof(Item))]
public int Id { get; set; }
But how can I do this via FluentAPI? I don't want data annotation in my Entitie Classes.
What you have is a One-to-one relationship with single navigation property, principal entity Item and dependent entity ItemStats, using the so called shared primary key association, where the dependent entity PK is also a FK to the principal entity.
Fluent API for one-to-one relationships are HasOne, WithOne, HasForeignKey and HasPrincipalKey. Please note that the generic type arguments to HasForeignKey and HasPrincipalKey (which normally are omitted for one-to-many relationship) here are important because they indentify which entity is principal and which - dependent.
With that being said, the fluent configuration for your model is:
modelBuilder.Entity<ItemStats>()
.HasOne(e => e.Item)
.WithOne()
.HasForeignKey<ItemStats>(e => e.Id);

What is the correct way to get an Entity with it's navigation property in Entity Framework Core?

How I get an Entity with it's navigation property in Entity Framework Core? I have seen in docs.microsoft that I have to use .Include() and it's working in a project but this time it's not working.
Model.cs
public class UniversityModel
{
public int ID { get; set; }
public string Name { get; set; }
public string LongDescription { get; set; }
public string Address { get; set; }
public List<UniSession> Sessions { get; set; }
public DateTime Date { get; set; }
public List<UniEmail> Emails { get; set; }
public List<UniPhone> Phones { get; set; }
}
And I'm accessing the UniversityModel with it's navigation property like.
UniversityModel university = await _context.Universities
.Include(u => u.Phones)
.Include(u => u.Emails)
.Include(u => u.Sessions)
.SingleOrDefaultAsync(u => u.ID == id);
It's getting the university correctly but Navigation properties are not including.
For make it clear look at the Model below all the navigation property models are same with foreign key of university.
public class UniEmail
{
public int ID { get; set; }
public int UniversityId { get; set; }
[StringLength(50)]
public string Email { get; set; }
}
What is the wrong how correctly I can Include all the navigation properties and If my Including code is wrong then how it's worked in another project?
The problem is that your entity model does not follow EF Core conventions and you don't use fluent configuration. Although EF discovers the relationship through collection navigation property, since there is no inverse reference navigation property and UniversityId field is not identified as FK, EF maps FK to a shadow property called UniversityModelId.
EF Core FK naming conventions are explained in the Fully Defined Relationships section of the documentation:
If the dependent entity contains a property named <primary key property name>, <navigation property name><primary key property name>, or <principal entity name><primary key property name> then it will be configured as the foreign key.
In other words, UniversityId will be considered conventionally as FK if:
(1) UniversityModel class is called University
(2) ID property of UniversityModel class is called UniversityID (and tagged with [Key] attribute because it would not match the PK convention)
(3) You add inverse navigation property called University:
public UniversityModel University { get; set; }
public int UniversityId { get; set; }
And of course it can be mapped explicitly with fluent API:
modelBuilder.Entity<UniversityModel>()
.HasMany(e => e.Emails) // with collection navigation property
.WithOne() // and no reference navigation property
.HasForeignKey(e => e.UniversityId); // and foreign key

The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations

"The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations."
I am getting this error in Entity Framework 4.4 when updating/migrating the database, but I am not trying to specify a 1:1 relationship. I want something like this:
public class EntityA
{
public int ID { get; set; }
public int EntityBID { get; set; }
[ForeignKey("EntityBID")]
public virtual EntityB EntityB { get; set; }
}
public class EntityB
{
public int ID { get; set; }
public Nullable<int> PreferredEntityAID { get; set; }
[ForeignKey("PreferredEntityAID")]
public virtual EntityA PreferredEntityA { get; set; }
}
where EntityA must have an EntityB parent, whereas EntityB can have a preferred EntityA child, but doesn't have to. The preferred child should be one of the children associated with the parent, but I don't know how to enforce this in the database. I'm planning on enforcing it programmatically.
How do I get around this error or what is a better way of accomplishing these relationships?
Entity Framework Code-First conventions are assuming that EntityA.EntityB and EntityB.PreferredEntityA belong to the same relationship and are the inverse navigation properties of each other. Because both navigation properties are references (not collections) EF infers a one-to-one relationship.
Since you actually want two one-to-many relationships you must override the conventions. With your model it's only possible with Fluent API:
modelBuilder.Entity<EntityA>()
.HasRequired(a => a.EntityB)
.WithMany()
.HasForeignKey(a => a.EntityBID);
modelBuilder.Entity<EntityB>()
.HasOptional(b => b.PreferredEntityA)
.WithMany()
.HasForeignKey(b => b.PreferredEntityAID);
(If you use this you can remove the [ForeignKey] attributes.)
You cannot specify a mapping that would ensure that the preferred child is always one of the associated childs.
If you don't want to use Fluent API but only data annotations you can add a collection property in EntityB and relate it to EntityA.EntityB using the [InverseProperty] attribute:
public class EntityB
{
public int ID { get; set; }
public Nullable<int> PreferredEntityAID { get; set; }
[ForeignKey("PreferredEntityAID")]
public virtual EntityA PreferredEntityA { get; set; }
[InverseProperty("EntityB")] // <- Navigation property name in EntityA
public virtual ICollection<EntityA> EntityAs { get; set; }
}

Unidirectional One-to-Many Associations in Entity Framework 4?

Does EF 4 support unidirectional one-to-many associations, as in:
public class Parent
{
public int Id { get; set; }
public string Something { get; set; }
public List<Child> AllMyChildren { get; set; }
}
public class Child
{
public int Id { get; set; }
public string Anotherthing { get; set; }
// I don't want a back-reference to the Parent!
// public int ParentId { get; set; }
}
When I try to compile my project with an association between Parent and Child where End2 Navigation is blank (because I unchecked the End2 Navigation Property checkbox in the Add Association dialog), I get
Error 2027: No mapping specified for the following EntitySet/AssociationSet - Child.
UPDATE:
And what if I just have a List or similar property on Parent rather than a List? Do I need to create a wrapping type to hold the String so that I can also hold a back-reference to Parent?
Belive that this would work using Fluent API (Only way of deifning unidirectional associations)
modelBuilder.Entity<Child>()
.HasKey(t => t.Id);
modelBuilder.Entity<Parent>()
.HasMany(p => p.AllMyChildren)
.WithRequiredPrincipal();
http://msdn.microsoft.com/en-us/library/hh295843(v=VS.103).aspx

Categories