Efcore: [OwnedType] Upgraded to ASP.NET Core 2.1.0 broke code

Created on 1 Jun 2018  路  8Comments  路  Source: dotnet/efcore

A class with an owned type that wasn't instantiated raise exception when added and to be saved.

Exception is something in the lines of The entity '' was adedd but no type of 'Phone' as marked as 'Added' ..

This isn't documented in the migration guid.

Steps to reproduce

```c#
public class Entity
{
public int Id { get; set; }

public Phone Phone { get; set; }

// This works
// public Phone Phone { get: set; } = Phone.Empty;
}

public class Phone
{
internal static readonly Phone Empty = new Phone();
private Phone() {/*ORM */ }

public Phone(/* params removed for brevity*/) {}

public string Prefix { get; private set; }

public string CountryCode { get; private set; }

public string Number { get; private set; }
}

public class EntityEntityTypeConfiguration : IEntityTypeConfiguration
{
public void Configure(EntityTypeBuilder builder)
{
builder.OwnsOne(_ => _.Phone);
}
}
```

Further technical details

EF Core version: 2.1.0
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10 Pro
IDE: Visual Studio 2017 15.7.3

closed-by-design customer-reported regression

Most helpful comment

The same here.

Do you know some workarround to solve this asap?

All 8 comments

@joacar Below is the code I used to attempt to reproduce this. Some observations:

  • The exception message I get is, "The entity of type 'Entity' is sharing the table 'Entity' with entities of type 'Phone', but there is no entity of this type with the same key value that has been marked as 'Added'."
  • The behavior is the same on 2.0 and 2.1.

It looks like you are seeing different behavior. Can you update my code or provide some other runnable project/solution or code listing that demonstrates the behavior you are seeing?

```C#
public class Entity
{
public int Id { get; set; }
public Phone Phone { get; set; }

// This works
// public Phone Phone { get: set; } = Phone.Empty;

}

public class Phone
{
internal static readonly Phone Empty = new Phone();

private Phone()
{
}

public string Prefix { get; private set; }
public string CountryCode { get; private set; }
public string Number { get; private set; }

}

public class BloggingContext : DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Server=(localdb)mssqllocaldb;Database=Test;ConnectRetryCount=0");
}

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Entity>().OwnsOne(_ => _.Phone);
}

}

public class Program
{
public static void Main()
{
using (var context = new BloggingContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
}

    using (var context = new BloggingContext())
    {
        context.Add(new Entity());
        context.SaveChanges();
    }
}

}
```

Had the same Problem as @joacar today when upgrading to 2.1 in our integration tests.
I have modified your code to reproduce my issue (might be the same as @joacar's).

The following line seems to be important to reproduce the issue:

ModelBuilder.Entity().OwnsOne(x => x.Address);

using Microsoft.EntityFrameworkCore;

public class Entity
{
    public Entity()
    {
        Address = new Address();
    }
    public int Id { get; set; }
    public Phone Phone { get; set; }
    public Address Address { get; private set; }
}

public class Phone
{
    public string Prefix { get; private set; }
}

public class Address
{
    public string Zip { get; private set; }
}

public class BloggingContext : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder
                .UseSqlServer(@"connectionString");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Entity>().OwnsOne(_ => _.Address);
        modelBuilder.Entity<Entity>().OwnsOne(_ => _.Phone);
    }
}

public class Program
{
    public static void Main()
    {
        using (var context = new BloggingContext())
        {
            context.Database.EnsureDeleted();
            context.Database.EnsureCreated();
        }

        using (var context = new BloggingContext())
        {
            var entity = new Entity();
            context.Add(entity);
            context.SaveChanges();
        }
    }
}

Verified this is regression. In 2.0.2 version if you don't have owned entity being added we saved owner only. In 2.1.0 we throw exception that owned entity is not present.

The same here.

Do you know some workarround to solve this asap?

Notes from triage: this was not supposed to be supported in 2.0.x, but the validation code failed to check multiple owned types. This was fixed for 2.1, resulting in the reports seen here. The problem with retaining the behavior for 2.0.x is that:

  • If the owned type contains some non-nullable property, then the insert will fail anyway. This means that adding a property to an owned type can cause code to fail when it wasn't failing before.
  • The type that is checked happens to be the first one found, which means the first one in alphabetical order. Adding a new owned type to the model could therefore change whether or not the code throws.
  • Since the validation is there because a null is not allowed it means that other parts of the code assume that null will not be found and hence if we continue not to validate this it could result in further failures due to the faulty assumption.

Therefore, we are planning to leave the 2.1 behavior as-is. The fix for applications is to assign an instance for the second owned type, just as is required for the first owned type.

The fix for applications is to assign an instance for the second owned type, just as is required for the first owned type.

Can some help me by translating that into code? What's the fix exactly? What's wrong with my configuration? 馃

@spottedmahn If you're adding the owner and the owned type and running into problems, then please file a new issue and include a small, runnable project/solution or complete code listing that demonstrates the behavior you are seeing.

Hi @ajcvickers - thanks for the reply 馃憤

I tracked down my issue to null owned types not being allowed.

Reference navigations to owned entity types cannot be null unless they are explicitly mapped to a separate table from the owner

Source

Was this page helpful?
0 / 5 - 0 ratings