Efcore: Migration issue with multiple "OwnsOne" properties of same type

Created on 26 Nov 2018  路  7Comments  路  Source: dotnet/efcore

I get the the following error message when I try to update the database after generating a migration. It works fine on 2.1. The issue appeared when I updated my TargetFramework to netcoreapp2.2.

The property 'AccountId' on entity type 'MigrationBugRepro.Models.Account.MainAddress#MigrationBugRepro.Models.Address' cannot be marked as nullable/optional because it has been included in a key {'AccountId'}.

Steps to reproduce

Given this DatabaseContext:

```c#
public class ApplicationContext : DbContext
{
public ApplicationContext(DbContextOptions options) : base(options)
{
}

    public virtual DbSet<Account> Accounts { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);

        builder.Entity<Account>(b =>
        {
            b.HasKey(x => x.AccountId);
            b.Property(e => e.AccountId).HasMaxLength(36).IsRequired();

            // Multiple Owned Types
            // Works if I only have one Owned address object, issue appears with 2 or more objects of the same type
            b.OwnsOne(e => e.MainAddress, a =>
            {
                a.Property(x => x.AddressLine1).HasMaxLength(256);
                a.Property(x => x.City).HasMaxLength(256);
            });
            b.OwnsOne(e => e.SecondaryAddress, a =>
            {
                a.Property(x => x.AddressLine1).HasMaxLength(256);
                a.Property(x => x.City).HasMaxLength(256);
            });
        });
    }

}


And those models:
```c#
public class Account
 {
     public string AccountId { get; set; }

     public Address MainAddress { get; set; }
     public Address SecondaryAddress { get; set; }
 }

 public class Address
 {
     public string AddressLine1 { get; set; }
     public string City { get; set; }
 }

You can find a sample Project here

Further technical details

EF Core version: 2.2.0-preview3-35497
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10
IDE: Visual Studio 2017 15.9.2

closed-fixed customer-reported regression type-bug

Most helpful comment

Note for triage: I am able to reproduce this on 2.2 preview3, but only when doing update-database after creating a migration. Conversly, EnsureCreated on the same code does not throw. Also does not throw if PK is changed from string to int.

Generated migration:
```C#
public partial class One : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Accounts",
columns: table => new
{
AccountId = table.Column(maxLength: 36, nullable: false),
MainAddress_AddressLine1 = table.Column(maxLength: 256, nullable: true),
MainAddress_City = table.Column(maxLength: 256, nullable: true),
SecondaryAddress_AddressLine1 = table.Column(maxLength: 256, nullable: true),
SecondaryAddress_City = table.Column(maxLength: 256, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Accounts", x => x.AccountId);
});
}

protected override void Down(MigrationBuilder migrationBuilder)
{
    migrationBuilder.DropTable(
        name: "Accounts");
}

}


Full stack trace:

PM> update-database
Applying migration '20181126214128_One'.
System.InvalidOperationException: The property 'AccountId' on entity type 'Account.MainAddress#Address' cannot be marked as nullable/optional because it has been included in a key {'AccountId'}.
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalPropertyBuilder.IsRequired(Boolean isRequired, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.IsRequired(Boolean isRequired, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.ReplaceForeignKey(InternalEntityTypeBuilder principalEntityTypeBuilder, InternalEntityTypeBuilder dependentEntityTypeBuilder, Nullable1 navigationToPrincipal, Nullable1 navigationToDependent, IReadOnlyList1 dependentProperties, IReadOnlyList1 principalProperties, Nullable1 isUnique, Nullable1 isRequired, Nullable1 isOwnership, Nullable1 deleteBehavior, Boolean removeCurrent, Boolean oldRelationshipInverted, Nullable1 principalEndConfigurationSource, Nullable1 configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.ReplaceForeignKey(Nullable1 configurationSource, InternalEntityTypeBuilder principalEntityTypeBuilder, InternalEntityTypeBuilder dependentEntityTypeBuilder, Nullable1 navigationToPrincipal, Nullable1 navigationToDependent, IReadOnlyList1 dependentProperties, IReadOnlyList1 principalProperties, Nullable1 isUnique, Nullable1 isRequired, Nullable1 isOwnership, Nullable1 deleteBehavior, Boolean removeCurrent, Nullable1 principalEndConfigurationSource, Boolean oldRelationshipInverted)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.Attach(InternalEntityTypeBuilder entityTypeBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Internal.RelationshipSnapshot.Attach(InternalEntityTypeBuilder entityTypeBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Internal.PropertiesSnapshot.Attach(InternalEntityTypeBuilder entityTypeBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.Snapshot.Attach(InternalEntityTypeBuilder entityTypeBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalModelBuilder.Entity(TypeIdentity& type, String definingNavigationName, EntityType definingEntityType, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalModelBuilder.Entity(String name, String definingNavigationName, EntityType definingEntityType, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.IsWeakTypeDefinition(ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.Owns(TypeIdentity& targetEntityType, PropertyIdentity navigation, Nullable1 inverse, ConfigurationSource configurationSource) at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.Owns(String targetEntityTypeName, String navigationName, ConfigurationSource configurationSource) at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder.OwnsOneBuilder(TypeIdentity& ownedType, String navigationName) at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder.OwnsOne(String ownedTypeName, String navigationName, Action1 buildAction)
at TwoTwoPreview.Migrations.One.<>c.b__2_1(EntityTypeBuilder b) in C:StuffTwoTwoPreviewTwoTwoPreviewMigrations20181126214128_One.Designer.cs:line 55
at Microsoft.EntityFrameworkCore.ModelBuilder.Entity(String name, Action1 buildAction) at TwoTwoPreview.Migrations.One.BuildTargetModel(ModelBuilder modelBuilder) in C:\Stuff\TwoTwoPreview\TwoTwoPreview\Migrations\20181126214128_One.Designer.cs:line 33 at Microsoft.EntityFrameworkCore.Migrations.Migration.<.ctor>b__4_0() at Microsoft.EntityFrameworkCore.Internal.LazyRef1.get_Value()
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.GenerateUpSql(Migration migration)
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The property 'AccountId' on entity type 'Account.MainAddress#Address' cannot be marked as nullable/optional because it has been included in a key {'AccountId'}.
PM>
```

All 7 comments

Note for triage: I am able to reproduce this on 2.2 preview3, but only when doing update-database after creating a migration. Conversly, EnsureCreated on the same code does not throw. Also does not throw if PK is changed from string to int.

Generated migration:
```C#
public partial class One : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Accounts",
columns: table => new
{
AccountId = table.Column(maxLength: 36, nullable: false),
MainAddress_AddressLine1 = table.Column(maxLength: 256, nullable: true),
MainAddress_City = table.Column(maxLength: 256, nullable: true),
SecondaryAddress_AddressLine1 = table.Column(maxLength: 256, nullable: true),
SecondaryAddress_City = table.Column(maxLength: 256, nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Accounts", x => x.AccountId);
});
}

protected override void Down(MigrationBuilder migrationBuilder)
{
    migrationBuilder.DropTable(
        name: "Accounts");
}

}


Full stack trace:

PM> update-database
Applying migration '20181126214128_One'.
System.InvalidOperationException: The property 'AccountId' on entity type 'Account.MainAddress#Address' cannot be marked as nullable/optional because it has been included in a key {'AccountId'}.
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalPropertyBuilder.IsRequired(Boolean isRequired, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.IsRequired(Boolean isRequired, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.ReplaceForeignKey(InternalEntityTypeBuilder principalEntityTypeBuilder, InternalEntityTypeBuilder dependentEntityTypeBuilder, Nullable1 navigationToPrincipal, Nullable1 navigationToDependent, IReadOnlyList1 dependentProperties, IReadOnlyList1 principalProperties, Nullable1 isUnique, Nullable1 isRequired, Nullable1 isOwnership, Nullable1 deleteBehavior, Boolean removeCurrent, Boolean oldRelationshipInverted, Nullable1 principalEndConfigurationSource, Nullable1 configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.ReplaceForeignKey(Nullable1 configurationSource, InternalEntityTypeBuilder principalEntityTypeBuilder, InternalEntityTypeBuilder dependentEntityTypeBuilder, Nullable1 navigationToPrincipal, Nullable1 navigationToDependent, IReadOnlyList1 dependentProperties, IReadOnlyList1 principalProperties, Nullable1 isUnique, Nullable1 isRequired, Nullable1 isOwnership, Nullable1 deleteBehavior, Boolean removeCurrent, Nullable1 principalEndConfigurationSource, Boolean oldRelationshipInverted)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.Attach(InternalEntityTypeBuilder entityTypeBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Internal.RelationshipSnapshot.Attach(InternalEntityTypeBuilder entityTypeBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Internal.PropertiesSnapshot.Attach(InternalEntityTypeBuilder entityTypeBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Internal.EntityType.Snapshot.Attach(InternalEntityTypeBuilder entityTypeBuilder)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalModelBuilder.Entity(TypeIdentity& type, String definingNavigationName, EntityType definingEntityType, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalModelBuilder.Entity(String name, String definingNavigationName, EntityType definingEntityType, ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalRelationshipBuilder.IsWeakTypeDefinition(ConfigurationSource configurationSource)
at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.Owns(TypeIdentity& targetEntityType, PropertyIdentity navigation, Nullable1 inverse, ConfigurationSource configurationSource) at Microsoft.EntityFrameworkCore.Metadata.Internal.InternalEntityTypeBuilder.Owns(String targetEntityTypeName, String navigationName, ConfigurationSource configurationSource) at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder.OwnsOneBuilder(TypeIdentity& ownedType, String navigationName) at Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder.OwnsOne(String ownedTypeName, String navigationName, Action1 buildAction)
at TwoTwoPreview.Migrations.One.<>c.b__2_1(EntityTypeBuilder b) in C:StuffTwoTwoPreviewTwoTwoPreviewMigrations20181126214128_One.Designer.cs:line 55
at Microsoft.EntityFrameworkCore.ModelBuilder.Entity(String name, Action1 buildAction) at TwoTwoPreview.Migrations.One.BuildTargetModel(ModelBuilder modelBuilder) in C:\Stuff\TwoTwoPreview\TwoTwoPreview\Migrations\20181126214128_One.Designer.cs:line 33 at Microsoft.EntityFrameworkCore.Migrations.Migration.<.ctor>b__4_0() at Microsoft.EntityFrameworkCore.Internal.LazyRef1.get_Value()
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.GenerateUpSql(Migration migration)
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String contextType)
at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action)
The property 'AccountId' on entity type 'Account.MainAddress#Address' cannot be marked as nullable/optional because it has been included in a key {'AccountId'}.
PM>
```

This is the code inside designer file which throws exception:
Putting below in OnModelCreating throws too. There does not seem to be anything particularly wrong with generated migration designer file. It seems like bug in model building code with shadow types.
```C#
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Configure model
modelBuilder.Entity("EFSampleApp.Account", b =>
{
b.Property("AccountId")
.ValueGeneratedOnAdd()
.HasMaxLength(36);

            b.HasKey("AccountId");
        });

        modelBuilder.Entity("EFSampleApp.Account", b =>
        {
            b.OwnsOne("EFSampleApp.Address", "MainAddress", b1 =>
            {
                b1.Property<string>("AccountId");

                b1.HasKey("AccountId");

                b1.HasOne("EFSampleApp.Account")
                    .WithOne("MainAddress")
                    .HasForeignKey("EFSampleApp.Address", "AccountId")
                    .OnDelete(DeleteBehavior.Cascade);
            });

            b.OwnsOne("EFSampleApp.Address", "SecondaryAddress", b1 =>
            {
                b1.Property<string>("AccountId");

                b1.HasKey("AccountId");

                b1.HasOne("EFSampleApp.Account")
                    .WithOne("SecondaryAddress")
                    .HasForeignKey("EFSampleApp.Address", "AccountId")
                    .OnDelete(DeleteBehavior.Cascade);
            });
        });
    }

```

We've found a workaround for this issue. There are actually two problems:

  1. Only 1 migration can be added, the subsequent ones will fail
  2. Scripting the migrations or updating the DB will fail.

For 1., edit ApplicationDbContextModelSnapshot.cs

Revert "ProductVersion" to "2.1.4-rtm-31024":

modelBuilder
  .HasAnnotation("ProductVersion", "2.1.4-rtm-31024" /* <-- This was "2.2.0-rtm-35687"  */)

In each OwnsOne block for the problematic owned property, remove the HasKey(...) line:

b.OwnsOne("EFSampleApp.Address", "MainAddress", b1 =>
{
  ...
  //b1.HasKey("AccountId");  <-- Comment or remove this line
   ...
});

You must do this for every new migration since this file is re-generated every time.


For 2., edit the generated xxxxxxxxxxxxxx_MyMigration.Designer.cs file

In each OwnsOne block for the problematic owned property, remove the HasKey(...) line:

b.OwnsOne("EFSampleApp.Address", "MainAddress", b1 =>
{
  ...
  //b1.HasKey("AccountId");  <-- Comment or remove this line
   ...
});

It's not necessary to change the "ProductVersion" in this file.

It didn't work for me. Is there any other thing that I can do?

@tiagor87 Are you referring to the fix checked in or the workaround in the previous comment?

@ajcvickers the workaround

@tiagor87 I just tried to reproduce the issue with servicing version 2.2.1-servicing-35743 of EF Core and it looks like this bug is fixed.
This is the package: https://dotnet.myget.org/feed/aspnetcore-dev/package/nuget/Microsoft.EntityFrameworkCore/2.2.1-servicing-35743
In order to use this version, you need to add the MyGet feed as a package source. You can do this by adding a Nuget.config file at the root of your solution:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="ASPNETCoreDevNightly" value="https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json" />
  </packageSources>
</configuration>

Hope this helps.

Was this page helpful?
0 / 5 - 0 ratings