dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.66k stars 3.16k forks source link

Unexpected "The object has been removed from the model." triggered by specific nesting and property naming in CosmosDb Provider #34329

Closed Smoovsky closed 6 days ago

Smoovsky commented 1 month ago

Today I ran into a weird InvalidOperationException with message "The object has been removed from the model.". And it took me hours to narrow down what triggers the exception. It turns out that when objects are nested in a specific pattern and with some uncertain property naming, the exception emerges. And the exception is specific to CosmosDb provider. It could be reproduced as (.NET Fiddle is also available):

using Microsoft.EntityFrameworkCore;

// using var cxt = new SqlServerCxt(); // sql server provider is fine

using var cxt = new CosmosDbCxt();

cxt.Find<EntityType1>(); // trigger building of the dbcontext

public class CosmosDbCxt : DbContext
{
    public DbSet<EntityType1> EntityType1 { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseCosmos( // you can stick with the fake values, the exception will be thrown prior to the connection
            "<edpt>",
            "<key>",
            "<dbName>");
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<EntityType1>().Ignore(x => x.E2); // exception thrown here
    }
}

// sql server provider is fine
// public class SqlServerCxt : DbContext
// {
//     public DbSet<EntityType1> EntityType1 { get; set; }

//     protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
//     {
//         optionsBuilder.UseSqlServer("Server=.");
//     }

//     protected override void OnModelCreating(ModelBuilder modelBuilder)
//     {
//         modelBuilder.Entity<EntityType1>().Ignore(x => x.E2);
//     }
// }

public class EntityType1
{
    public EntityType2 E2 { get; set; }
}

public class EntityType2
{
    public EntityType3 E3 { get; set; }

    public EntityType4 E4 { get; set; }
}

public class EntityType3
{
    public EntityType2 U2 { get; set; } // changing the property name here will either fix or break the issue, name like 'E2', 'A' is fine, but name like 'U2', 'Exy' is not. I could not find a pattern here
}

public class EntityType4
{
    public EntityType3? E3 { get; set; }
}

EF Core version: 8.0.7 Database provider: Microsoft.EntityFrameworkCore.Cosmos Target framework: NET 8.0 Operating system: Windows 11 Pro 23H2 Build 22631.3958

AndriySvyryd commented 1 month ago

This will be fixed in 9.0, but we don't think this affects enough users to meet our patch bar.

As a workaround you can ignore the target entity type first:

modelBuilder.Ignore<EntityType2>();
modelBuilder.Entity<EntityType1>().Ignore(x => x.E2); 

If it still should be refenced from other entity types you can configure those navigations explicitly afterwards.