Max Max - 1 month ago 23
C# Question

Create Self-Referencing Hierarchical Table in Entity Framework

Background

I have a class that looks more or less like this:

public class MyClass
{
[Id]
public long Id { get; set; }

public string MyProperty { get; set; }
public bool MyBoolean { get; set; }
public string AnotherProperty { get; set; }

public MyClass ChildOne { get; set; }
public MyClass ChildTwo { get; set; }
}


I will need to use a stored procedure to load a set of records, but that's ok as long as the structure itself is correct.

For any instance of
MyClass
, one or both of the children can be null. Any instance of
MyClass
can be used in a parent class - but the child itself doesn't need to know about this relationship, and a child can be used by any number of parents.

Problem

With this structure, I get the following error when creating a new migration:


Unable to determine the principal end of an association between the
types 'MyClass' and 'MyClass'. The principal end of this association
must be explicitly configured using either the relationship fluent API
or data annotations.


This error makes sense - when given a structure of an object with a foreign key to itself, I am not surprised that EF has a hard time determining the principal end. I'm not sure how to fix this, though.

I've tried some different Fluent mappings:

modelBuilder.Entity<MyClass>().HasOptional(x => x.ChildOne).WithOptionalPrincipal(x => x.ChildOne);
modelBuilder.Entity<MyClass>().HasOptional(x => x.ChildOne).WithOptionalDependent(x => x.ChildOne);
modelBuilder.Entity<MyClass>().HasOptional(x => x.ChildOne);


(Note: I didn't try these concurrently - I did one at a time & duplicated it for ChildTwo.)

I was able to get a migration to work by adding a
ChildThree
property to MyClass, but that doesn't make sense and isn't a useful property; it just creates another foreign key on the table but this isn't needed in my model.

So, in summary:


  1. How do I get this structure to work the way I want? I think the secret is in some Fluent mapping voodoo but I'm very unfamiliar with that library and I don't know how to get that to work.

  2. Why does adding a third (unneeded, unwanted) property fix everything and allow the migration to scaffold?


Answer

Your fluent mapping is totally wrong.

You should do something like this:

modelBuilder.Entity<MyClass>().HasOptional(p => p.ChildOne).WithOptionalDependent();
modelBuilder.Entity<MyClass>().HasOptional(p => p.ChildTwo).WithOptionalDependent();