I have a self-referencing relationship for the Contacts model, e.g. a contact can have a relationship with another contact. In the Controller GET: Details method I have this code:
var contact = await _context.Contacts
.Include(r => r.RelationshipsAsParent)
.ThenInclude(t => t.RelationshipType)
.FirstOrDefaultAsync(m => m.Id == id);
The debugger shows the RelationshipsAsParent.Child as null

I can get the ChildId, but there should be the "FullName" of the Contact under the child.
The GET: Index query shows it as not null, so I can access the "FullName".
return _context.Contacts != null ?
View(await _context.Contacts
.Include(r => r.RelationshipsAsParent)
.ThenInclude(t => t.RelationshipType)
.ToListAsync()) :
Problem("Entity set 'ApplicationDbContext.Contacts' is null.");

I can't figure out why the Child is null for the Details method, but not for the Index method.
Thanks in advance for any help :)
Models
public class Contact
{
public int Id { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
[NotMapped]
public string? FullName { get { return $"{FirstName} {LastName}"; } }
// Navigation properties for relationships where this contact is the parent
public virtual List? RelationshipsAsParent { get; set; }
// Navigation properties for relationships where this contact is the child
public virtual List? RelationshipsAsChild { get; set; }
}
public class Relationship
{
public int Id { get; set; }
public int? ParentId { get; set; }
public virtual Contact? Parent { get; set; }
// Foreign key and navigation property for the child contact
public int? ChildId { get; set; }
public virtual Contact? Child { get; set; }
// Navigation property for the relationship type (assuming there's a RelationshipType entity)
public int? RelationshipTypeId { get; set; }
public virtual RelationshipType? RelationshipType { get; set; }
}
public class RelationshipType
{
public int Id { get; set; }
public string? Type { get; set; }
public virtual List? Relationships { get; set; } = new List();
}
Fluent API
modelBuilder.Entity(b =>
{
modelBuilder.Entity()
.HasOne(r => r.Parent)
.WithMany(c => c.RelationshipsAsParent)
.HasForeignKey(r => r.ParentId)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity()
.HasOne(r => r.Child)
.WithMany(c => c.RelationshipsAsChild)
.HasForeignKey(r => r.ChildId)
.OnDelete(DeleteBehavior.Restrict);
});
Detail View
Relationships
@if (@Model.RelationshipsAsParent.Count > 0)
{
@for (int j = 0; j < Model.RelationshipsAsParent.Count; j++)
{
- @Model.RelationshipsAsParent[j].ChildId: @Model.RelationshipsAsParent[j].RelationshipType.Type
}
}
else
{
- No Relationships
}
Index View
@if (item.RelationshipsAsParent != null)
{
@for (int j = 0; j < item.RelationshipsAsParent.Count; j++)
{
- @item.RelationshipsAsParent[j].Child.FullName: @item.RelationshipsAsParent[j].RelationshipType.Type
}
}
else
{
- No Relationships
}
Prasad RaveendranPosted Nov 25, 2023, 12:27 AM
In the Details method, ensure that the Child is loaded along with the RelationshipType when retrieving the Relationship data. Entity Framework might not eagerly load related entities unless explicitly instructed to do so.
Here's a revised version of your Details method that includes eager loading of the Child and RelationshipType:
By using
.Include()withr => r.Childandr => r.RelationshipType, you instruct Entity Framework to eagerly load the Child and RelationshipType entities along with the Relationship entity when fetching data from the database.This should ensure that when you access the Child or RelationshipType properties in the Details view, they won't be null, provided that valid data exists in the database for the specified Relationship ID.
Remember, when working with Entity Framework and navigation properties, explicitly specifying
.Include()for related entities helps in eager loading and avoids null references in views or methods due to lazy loading or missing related entities in the retrieved data.