📙
Learn EF Core 9
  • Introduction of Entity Framework Core
  • EF Core - AI Tools
  • What's New in EF Core 5
    • Simple Logging
    • Filtered Included
    • Backing Fields
    • Keyless Entity Types
    • Configure Precision and Scale
    • Translation of Contains on byte arrays
    • Many-to-many Relationship
    • Table-per-type (TPT) mapping
    • Required one-to-one Dependents
    • Support for Fields using Lambda
    • Drop Column from SQLite Database
    • Index Attribute
  • BulkExtensions in EF Core
  • Connection Strings: Entity Framework Core
  • Entity Framework Core Model
  • DbContext
  • DbSet
  • Relationship in EF-Core
  • Lazy Loading in EF Core
  • Migrations in EF-Core
  • Handling Concurrency in EF-Core
  • Raw SQL Queries in EF-Core
  • Database Providers
    • SQL Server
    • SQLite
    • InMemory
    • Cosmos
    • PostgreSQL
  • Project Types
    • Console
    • MVC
    • WinForm
    • Xamarin
    • Blazor
Powered by GitBook
On this page
  1. What's New in EF Core 5

Required one-to-one Dependents

PreviousTable-per-type (TPT) mappingNextSupport for Fields using Lambda

Last updated 3 years ago

Required one-to-one Dependents

EF Core allows you to model entity types that can only ever appear on navigation properties of other entity types. These are called owned entity types. The entity containing an owned entity type is its owner.

  • In EF Core 3.1, the dependent end of a one-to-one relationship was always considered optional.

  • This was most apparent when using owned entities.

Let's consider the following model.

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }

    public Address HomeAddress { get; set; }
    public Address BillingAddress { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Country { get; set; }
}

Here is the implementation of the context class which contains the configuration.

public class EntityContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder opBuilder)
    {
        opBuilder.UseSqlServer("Data Source=(localdb)\\ProjectsV13;Initial Catalog=PeopleContextDb1;");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Customer>(b =>
        {
            b.OwnsOne(e => e.HomeAddress);

            b.OwnsOne(e => e.BillingAddress,
                b =>
                {
                    b.Property(e => e.Street).IsRequired();
                    b.Property(e => e.City).IsRequired();
                    b.Property(e => e.State).IsRequired();
                });

        });
    }

    public DbSet<Customer> Customers { get; set; }
}

When migrations or EnsureCreated are used to create the database. On SQL Server, it will translate to the following SQL.

CREATE TABLE [dbo].[Customers] (
    [Id]                     INT            IDENTITY (1, 1) NOT NULL,
    [Name]                   NVARCHAR (MAX) NULL,
    [HomeAddress_Street]     NVARCHAR (MAX) NULL,
    [HomeAddress_City]       NVARCHAR (MAX) NULL,
    [HomeAddress_State]      NVARCHAR (MAX) NULL,
    [HomeAddress_Country]    NVARCHAR (MAX) NULL,
    [BillingAddress_Street]  NVARCHAR (MAX) NULL,
    [BillingAddress_City]    NVARCHAR (MAX) NULL,
    [BillingAddress_State]   NVARCHAR (MAX) NULL,
    [BillingAddress_Country] NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED ([Id] ASC)
);

As you can see all the columns are nullable, even though some of the BillingAddress properties have been configured as required.

  • When you query for a Customer, and all the columns for any of the addresses are null.

  • EF Core will leave all the properties of that address null, instead of setting an empty instance of address.

In EF Core 5.0, the BillingAddress navigation can now be configured as a required dependent.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Customer>(b =>
    {                
        b.OwnsOne(e => e.HomeAddress);

        b.OwnsOne(e => e.BillingAddress,
            b =>
            {
                b.Property(e => e.Street).IsRequired();
                b.Property(e => e.City).IsRequired();
                b.Property(e => e.State).IsRequired();
            });
        b.Navigation(e => e.BillingAddress).IsRequired();
    });
}

Now when you create migration or call the EnsureCreated method, you will see that it will now include non-nullable columns for the required properties of the required dependent.

CREATE TABLE [dbo].[Customers] (
    [Id]                     INT            IDENTITY (1, 1) NOT NULL,
    [Name]                   NVARCHAR (MAX) NULL,
    [HomeAddress_Street]     NVARCHAR (MAX) NULL,
    [HomeAddress_City]       NVARCHAR (MAX) NULL,
    [HomeAddress_State]      NVARCHAR (MAX) NULL,
    [HomeAddress_Country]    NVARCHAR (MAX) NULL,
    [BillingAddress_Street]  NVARCHAR (MAX) NOT NULL,
    [BillingAddress_City]    NVARCHAR (MAX) NOT NULL,
    [BillingAddress_State]   NVARCHAR (MAX) NOT NULL,
    [BillingAddress_Country] NVARCHAR (MAX) NULL,
    CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED ([Id] ASC)
);

EF Core will now throw an exception if an attempt is made to save a customer with a nullBillingAddress.

Improve EF Core performance with EF Extensions