Hello,
I’m working on a multi-project architecture with models and data access layers in separate .csproj
files (These are Classes Libraries.) Using EF Core 8 and TPH inheritance, I encounter the following error when running Add-Migration
in VS2022:
Unable to create a 'DbContext' of type 'ContentDbContext'. The exception 'The collection navigation 'Paragraphs' cannot be added to the entity type 'Summary' because its CLR type 'ICollection<SummaryParagraph>' does not implement 'IEnumerable<ParagraphBase>'. Collection navigations must implement IEnumerable<> of the related entity.'
This seems related to my TPH configuration. Below is an outline of the code setup.
Relevant Code
Model Interfaces
public interface IParagraph
{
string Content { get; set; }
}
public interface IHasParagraphs<T> where T : IParagraph
{
ICollection<T> Paragraphs { get; set; }
}
Abstract Base Class
public abstract class ParagraphBase : TopicStructureBase, IOrderable, IParagraph
{
public enum ParagraphParentType { ParagraphBlock, Summary }
public ParagraphParentType? ParentType { get; set; }
public int DisplayOrder { get; set; }
public string Content { get; set; } = string.Empty;
}
Derived Classes
public class SummaryParagraph : ParagraphBase
{
public Guid SummaryId { get; set; }
public virtual Summary Summary { get; set; } = new();
}
public class ContentParagraph : ParagraphBase
{
public Guid ParagraphBlockId { get; set; }
public virtual ParagraphBlock Block { get; set; } = new();
}
Aggregates
public class Summary : IdentifiableEntityBase, IHasParagraphs<SummaryParagraph>
{
public virtual ICollection<SummaryParagraph> Paragraphs { get; set; } = new HashSet<SummaryParagraph>();
}
public class ParagraphBlock : TopicElementBase, IHasParagraphs<ContentParagraph>
{
public virtual ICollection<ContentParagraph> Paragraphs { get; set; } = new HashSet<ContentParagraph>();
}
Entity Configuration
public void Configure(EntityTypeBuilder<ParagraphBase> builder)
{
builder.HasDiscriminator(p => p.ParentType)
.HasValue<ContentParagraph>(ParagraphParentType.ParagraphBlock)
.HasValue<SummaryParagraph>(ParagraphParentType.Summary);
builder.HasOne<Summary>()
.WithMany(s => s.Paragraphs)
.HasForeignKey(sp => ((SummaryParagraph)sp).SummaryId);
builder.HasOne<ParagraphBlock>()
.WithMany(pb => pb.Paragraphs)
.HasForeignKey(cp => ((ContentParagraph)cp).ParagraphBlockId);
}
What I’ve Tried
- Moving foreign key properties (
SummaryId
, ParagraphBlockId
) and navigation properties into the base class. This resolves the error but breaks specialization, as it forces all derived classes to share these properties.
- Simplifying the configuration by defining relationships directly in
OnModelCreating
. This avoids IEntityTypeConfiguration<T>
but does not address the root issue.
- Searching online and using tools like ChatGPT, but without finding a definitive answer.
What I Need Help With
- Foreign Key Properties: Must
SummaryId
and ParagraphBlockId
be in the base class for TPH to work? If so, why does EF Core require this?
- Compatibility Issues: Are there changes in EF Core 9 that could affect this configuration?
- Alternative Approaches: Is there a way to resolve this issue while maintaining proper separation of concerns?