One-to-one relationships with Entity Framework

c# entity-framework-core

May 13 2024 03:40

One-to-one relationships are used when one entity is associated with at most one other entity. For example, a ProductEntity has one ProductDetailEntity, and a ProductDetailEntity belongs to only one ProductEntity

In this example, ProductDetails links to Product via ProductId (foreign key) which is primary key in Products table

ProductId is both primary/foreign key of ProductDetails table

Config unidirectional relationship

A unidirectional relationship has only an owning side

public class ProductEntity
{
    public int Id { get; set; }
    public required string Url { get; set; }
    public required string Name { get; set; }
    public string? Description { get; set; }
    public decimal Price { get; set; }
    public DateTimeOffset CreatedDate { get; set; }
    
    public virtual ProductDetailEntity? Details { get; set; }

}

public class ProductDetailEntity
{
    public int ProductId { get; set; }
    public int Width { get; init; }
    public int Height { get; init; }
    public int Depth { get; init; }
    public decimal Weight { get; init; }
}

// Fluent API config in DBContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<ProductEntity>(
        e =>
        {
            e.ToTable("Products");
            e.HasKey(x => new { x.Id }).HasName("PK_ProductId");
            e.Property(x => x.Id).ValueGeneratedOnAdd();
            e.Property(x => x.Url).HasMaxLength(255);
            e.Property(x => x.Name).HasMaxLength(255);
            e.Property(x => x.Price).HasPrecision(18, 2);
            e.Property(x => x.CreatedDate).HasColumnType("datetimeoffset");
            e.Property(x => x.Description).HasColumnType("text").HasMaxLength(255);

            // Relation config
            e.HasOne(product => product.Details)
               .WithOne()
               .HasForeignKey<ProductDetailEntity>(productDetail => productDetail.ProductId)
               .IsRequired(false);
        }
    );
}

Enable bidirectional relationship

Bidirectional relationships allow us to navigate to the child/parent entity from the current entity

public class ProductDetailEntity
{
    public int ProductId { get; set; }
    public int Width { get; init; }
    public int Height { get; init; }
    public int Depth { get; init; }
    public decimal Weight { get; init; }

    // Add parent entity
    public virtual ProductEntity Product { get; init; } = null!;
}

Relation config change to

e.HasOne(product => product.Details)
    .WithOne(productDetail => productDetail.Product)
    .HasForeignKey<ProductDetailEntity>(productDetail => productDetail.ProductId)
    .IsRequired();

SQL generated by Entity Framework core

SELECT [p].[Id], [p].[CreatedDate], [p].[Description], [p].[Name], [p].[Price], [p].[Url], [p0].[ProductId], [p0].[Depth], [p0].[Height], [p0].[Weight], [p0].[Width]
FROM [Products] AS [p]
LEFT JOIN [ProductDetails] AS [p0] ON [p].[Id] = [p0].[ProductId]

More details from Microsoft document: One-to-one relationships

Full project example available on Github

me

Pham Duc Minh

Da Nang, Vietnam