dotnet / dotnet/efcore

Can't compare nested owned entities to inline/parameter

Open
#36,400 1 comment 0 reactions 0 assignees View on GitHub
area-owned-entities area-query
Dominant language
C#
Stars
14.8k
Forks
3.4k
PR merge metrics
PR metrics pending

Description

Comparing a nested owned entity to another works, but trying to compare it to a parameter or inline (constant) fails:

```c#
// This (entity equality with inline) throws:
_ = await context.Entities
.Where(e => e.Related.Nested1 == new Nested { Foo = 42 })
.ToListAsync();

// This (entity equality with parameter) also throws:
var nested = new Nested { Foo = 42 };
_ = await context.Entities
.Where(e => e.Related.Nested1 == nested)
.ToListAsync();
```

Exception:

```sql
Unhandled exception. System.InvalidOperationException: No backing field could be found for property 'Related.Nested1#Nested (Nested).RelatedEntityId' and the property does not have a getter.
at Microsoft.EntityFrameworkCore.Metadata.IPropertyBase.GetMemberInfo(Boolean forMaterialization, Boolean forSet)
at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrPropertyGetterFactory.GetMemberInfo(IPropertyBase propertyBase)
at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrAccessorFactory`1.CreateBase(IPropertyBase propertyBase)
at Microsoft.EntityFrameworkCore.Metadata.Internal.ClrPropertyGetterFactory.Create(IPropertyBase property)
at Microsoft.EntityFrameworkCore.Metadata.RuntimePropertyBase.<>c.b__43_0(RuntimePropertyBase property)
at Microsoft.EntityFrameworkCore.Internal.NonCapturingLazyInitializer.EnsureInitialized[TParam,TValue](TValue& target, TParam param, Func`2 valueFactory)
at Microsoft.EntityFrameworkCore.Metadata.RuntimePropertyBase.Microsoft.EntityFrameworkCore.Metadata.IPropertyBase.GetGetter()
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.CreatePropertyAccessExpression(Expression target, IProperty property)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.<>c__DisplayClass57_0.b__2(IProperty p)
```

Full repro

```c#
await using var context = new BlogContext();
await context.Database.EnsureDeletedAsync();
await context.Database.EnsureCreatedAsync();

context.Entities.Add(new Entity
{
Related = new Related
{
Nested1 = new Nested { Foo = 42 },
Nested2 = new Nested { Foo = 43 }
}
});
await context.SaveChangesAsync();

// The following "works" as designed: this translates to WHERE 0 = 1, since owned entities are compared by reference
// and therefore can never be equal.
_ = await context.Entities
.Where(e => e.Related.Nested1 == e.Related.Nested2)
.ToListAsync();

// This (entity equality with inline) throws:
_ = await context.Entities
.Where(e => e.Related.Nested1 == new Nested { Foo = 42 })
.ToListAsync();

// This (entity equality with parameter) also throws:
var nested = new Nested { Foo = 42 };
_ = await context.Entities
.Where(e => e.Related.Nested1 == nested)
.ToListAsync();

public class BlogContext : DbContext
{
public DbSet Entities { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseSqlServer("Server=localhost;Database=test;User=SA;Password=Abcd5678;Connect Timeout=60;ConnectRetryCount=0;Encrypt=false")
.LogTo(Console.WriteLine, LogLevel.Information)
.EnableSensitiveDataLogging();

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity()
.OwnsOne(b => b.Related, o =>
{
// o.ToJson(); // Can map to JSON or not, doesn't matter
o.OwnsOne(r => r.Nested1);
o.OwnsOne(r => r.Nested2);
});
}
}

public class Entity
{
public int Id { get; set; }

public Related Related { get; set; } = null!;
}

public class Related
{
public Nested Nested1 { get; set; } = null!;
public Nested Nested2 { get; set; } = null!;
}

public class Nested
{
public int Foo { get; set; }
}
```

Tests: Nested_with_inline, Nested_with_parameter, Nested_collection_with_inline, Nested_collection_with_parameter in OwnedNavigationsStructuralEqualityTestBase

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.