Although this post is for Entity Framework not Entity Framework Core, It might be useful for someone who wants to achieve the same thing using Entity Framework Core (I am using V1.1.2).
I don't need navigation properties (although they're nice) because I am practicing DDD and I want Parent and Child to be two separate aggregate roots. I want them to be able to talk to each other via foreign key not through infrastructure-specific Entity Framework navigation properties.
All you have to do is to configure the relationship on one side using HasOne and WithMany without specifying the navigation properties (they're not there after all).
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) {}
protected override void OnModelCreating(ModelBuilder builder)
{
......
builder.Entity<Parent>(b => {
b.HasKey(p => p.Id);
b.ToTable("Parent");
});
builder.Entity<Child>(b => {
b.HasKey(c => c.Id);
b.Property(c => c.ParentId).IsRequired();
// Without referencing navigation properties (they're not there anyway)
b.HasOne<Parent>() // <---
.WithMany() // <---
.HasForeignKey(c => c.ParentId);
// Just for comparison, with navigation properties defined,
// (let's say you call it Parent in the Child class and Children
// collection in Parent class), you might have to configure them
// like:
// b.HasOne(c => c.Parent)
// .WithMany(p => p.Children)
// .HasForeignKey(c => c.ParentId);
b.ToTable("Child");
});
......
}
}
I am giving out examples on how to configure entity properties as well, but the most important one here is HasOne<>, WithMany() and HasForeignKey().
Multiple bounded contexts scenario
If you have your Entity Framework entities defined in multiple projects (trying to follow BoundedContext pattern), b.HasOne<Parent>() won't work either!
For example, you have an entity called VehicleType from one project, and ServicePricing entity from another project, and they're one-to-many: one vehicle type can have multiple pricings (because they're versioned):
namespace DL.VehicleManagement.Data.EFCore.Entities
{
public class VehicleType
{
public Guid Id { get; set; }
public string Name { get; set; } = null!;
...
}
}
namespace DL.Pricing.Data.EFCore.Entities
{
public class ServicePricing
{
public Guid Id { get; set; }
public Guid VehicleTypeId { get; set; }
public int Version { get; set; }
...
}
}
If they were in one project, you can set their relationship even without using navigation properties as described at the beginning:
public class ServicePricingConfiguration : IEntityTypeConfiguration<ServicePricing>
{
public void Configure(EntityTypeBuilder<ServicePricing> builder)
{
builder.HasKey(x => x.Id);
// Setup composite key
builder.HasAlternateKey(x => new { x.VehicleTypeId, x.Version });
builder.HasOne<VehicleType>()
.WithMany()
.HasForeignKey(x => x.VehicleTypeId);
...
}
}
However, that would give you errors when those entities exist in different projects.
But if you think about it twice, you shouldn't import VehicleManagement.Data.EFCore into Pricing.Data.EFCore (hard referencing) anyway, just because they're in two different contexts!
What I can do is to leave the VehicleTypeId in ServicePricing as it is. I can still query a collection of service pricings by the vehicle type Id (soft referencing).
I guess the last thing I can do on VehicleTypeId is to setup indexes
on it, which normally setting up as foreign key would do too:
public class ServicePricingConfiguration : IEntityTypeConfiguration<ServicePricing>
{
public void Configure(EntityTypeBuilder<ServicePricing> builder)
{
builder.HasKey(x => x.Id);
// Setup composite key
builder.HasAlternateKey(x => new { x.VehicleTypeId, x.Version });
builder.HasIndex(x => x.VehicleTypeId)
.IsUnique(false)
.IsClustered(false);
...
}
}