Reputation: 135
Hoping someone might be able to answer this as I am struggling to find a decent answer. It's been a while since I've poked around with EF, especially the core variant.
Basically given the following tables:
CREATE TABLE [Documents] (
[Id] BIGINT NOT NULL IDENTITY(1,1),
[Name] NVARCHAR(255) NOT NULL,
CONSTRAINT [PK_Documents] PRIMARY KEY CLUSTERED ([Id] ASC))
CREATE TABLE [DocumentDependencies] (
[DocumentId] BIGINT NOT NULL,
[DependentOnDocumentId] BIGINT NOT NULL,
CONSTRAINT [PK_DocumentDependencies] PRIMARY KEY CLUSTERED ([DocumentId] ASC, [DependentOnDocumentId] ASC),
CONSTRAINT [FK_DocumentDependencies_Documents] FOREIGN KEY ([DocumentId]) REFERENCES [Documents]([Id]),
CONSTRAINT [FK_DocumentDependencies_DocumentDependencies] FOREIGN KEY ([DependentOnDocumentId]) REFERENCES [Documents]([Id]))
Then I have a model of:
public class Document {
public long Id { get; set; }
public IEnumerable<Document> DependentDocuments { get; set; }
}
The relationship being defined as, one document can have 0 or more dependent documents as defined by the mapping table. However I am unable to find a way of mapping this in EFCore without having to create a new object for the DependentDocument which I'd rather not do.
Any-one got any ideas? Thanks. Tried searching for an appropriate answer but can't find anything that really matches it. Perhaps my architecture is wrong but no harm asking for other opinions.
[Updates] After some digging around and messing with implementations based on Martinauts answer below I came up with this:
public class DocumentEntityTypeConfiguration : IEntityTypeConfiguration<Document>
{
public void Configure(EntityTypeBuilder<Document> builder)
{
builder.ToTable("Documents");
builder.HasKey(e => e.Id); js));
builder.HasMany(d => d.DependentDocuments)
.WithMany(p => p.InverseDependentDocuments)
.UsingEntity<Dictionary<string, object>>(
"DependentDocument",
r => r
.HasOne<Document>()
.WithMany()
.HasForeignKey("DocumentId")
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_DependentDocuments_MainDocument"),
l => l
.HasOne<Document>()
.WithMany()
.HasForeignKey("DependentDocumentId")
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_DependentDocuments_DependentDocument"),
j =>
{
j.HasKey("DocumentId", "DependentDocumentId");
j.ToTable("DependentDocuments");
});
builder.HasMany(d => d.InverseDependentDocuments)
.WithMany(p => p.DependentDocuments)
.UsingEntity<Dictionary<string, object>>(
"DependentDocument",
r => r
.HasOne<Document>()
.WithMany()
.HasForeignKey("DependentDocumentId")
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_DependentDocuments_DependentDocument"),
l => l
.HasOne<Document>()
.WithMany()
.HasForeignKey("DocumentId")
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_DependentDocuments_MainDocument"),
j =>
{
j.HasKey("DocumentId", "DependentDocumentId");
j.ToTable("DependentDocuments");
});
}
}
using the above configuration alone it does not work unfortunately. However, if I then add the NuGet Package Microsoft.EntityFrameworkCore.Proxies and enable LazyLoading, it does indeed retrieve the dependency documents, however only for the Inverse collection. Anyone got any clue why it won't work without lazy loading? I've tried enumerating the lists to get the associated documents but it doesn't seem to work without that LazyLoading feature enabled on the context.
[Update #2] Actually got it working, so the code works as per suggested answer but need to remember to include the dataset as well :/
Upvotes: 0
Views: 1453
Reputation: 2297
Add the InverseDependentDocuments
property to the Document
-class.
public class Document {
public long Id { get; set; }
public IEnumerable<Document> DependentDocuments { get; set; }
public IEnumerable<Document> InverseDependentDocuments { get; set; }
}
You can configure the relationship using the model builder in your DbContext
class in the OnModelCreating
method:
modelBuilder.Entity<Document>
.HasMany(x => x.DependentDocuments)
.WithMany(x => x.InverseDependentDocuments)
.usingEntity(x => {
x.ToTable("DocumentDependencies")
// ... additional configuration for column naming
});
This is also explained in the documentation.
Upvotes: 1