tabs ↹ over ␣ ␣ ␣ spaces

by Jiří {x2} Činčura

Mapping self references in Code First

20 Dec 2011 2 mins Entity Framework

From time to time I see people having problems to map self references in Code First in Entity Framework. It might be confusing what to do with HasMany/WithMany and HasOptional/WithOptional.

So let’s jump in. Define a pretty simple table:

create table SelfRefs (
  ID int primary key,
  FooBar varchar(20) not null,
  ID_Parent int
);
alter table SelfRefs add foreign key (ID_Parent) references SelfRefs(ID);

The corresponding class could be:

class SelfRef
{
	public int ID { get; set; }
	public string FooBar { get; set; }
	public SelfRef ParentItem { get; set; }
	public int? ParentItemID { get; set; }
	public ICollection<SelfRef> ChildItems { get; set; }
}

Now the mapping. The “trick” here is to realize, that we have not only child->parent association, but also parent->children. The other one is implied from the first one. Hence every child item has optional parent. If the child item has no parent, it is actually a 1st level child, aka child of (invisible) “root” (NULL parent). That also means you can map it from both directions and result will be the same. Here’s the example (it’s explicit a little more) with both mappings (you can use both declarations together without any problem):

class SelfRefConfiguration : EntityTypeConfiguration<SelfRef>
{
	public SelfRefConfiguration()
	{
		this.HasKey(x => x.ID);
		this.Property(x => x.ParentItemID).HasColumnName("ID_Parent");
		// starting from child with parent
		//this.HasOptional(x => x.ParentItem).WithMany(x => x.ChildItems).HasForeignKey(x => x.ParentItemID).WillCascadeOnDelete(false);
		// starting from parent with children
		this.HasMany(x => x.ChildItems).WithOptional(x => x.ParentItem).HasForeignKey(x => x.ParentItemID).WillCascadeOnDelete(false);
		this.Map(map =>
			{
				map.Properties(x => new { x.ID, x.FooBar, x.ParentItemID });
				map.ToTable("SelfRefs", "dbo");
			});
	}
}

Hope I made it a little bit more clear. If not, feel free to ask in comments.

Profile Picture Jiří Činčura is .NET, C# and Firebird expert. He focuses on data and business layers, language constructs, parallelism, databases and performance. For almost two decades he contributes to open-source, i.e. FirebirdClient. He works as a senior software engineer for Microsoft. Frequent speaker and blogger at www.tabsoverspaces.com.