2

In Entity Framework Core 3.1.3, I have used the value object feature. In the query side, the problem is an extra left join exists in T-SQL. This extra join results in problems in terms of performance. In the following code Student is an Entity type and Address class is a value type.

Entities

public class Student
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public string City { get; set; }
    public string ZipCode { get; set; }
}

DbContext

public class ApplicationDbContext : DbContext
{
    public DbSet<Student> Students { get; set; }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<Student>().OwnsOne(e => e.Address);
        base.OnModelCreating(builder);
    }
}

Entity Framework query

var list = _dbContext.Students.ToList();

Generated T-SQL for this EF query:

SELECT [s].[Id], [s].[Name], [t].[Id], [t].[Address_City], 
       [t].[Address_Street], [t].[Address_ZipCode]
FROM [Students] AS [s]
LEFT JOIN (
    SELECT [s0].[Id], [s0].[Address_City], 
           [s0].[Address_Street], [s0].[Address_ZipCode]
    FROM [Students] AS [s0]
    WHERE [s0].[Address_ZipCode] IS NOT NULL OR 
          ([s0].[Address_Street] IS NOT NULL OR 
          [s0].[Address_City] IS NOT NULL)
) AS [t] ON [s].[Id] = [t].[Id]
Mohammad Akbari
  • 4,486
  • 6
  • 43
  • 74
  • get rid of the OnModelCreating method (or implementation) ... with properly structured entity, that should be enough! ... also you might wanna add public int StudentId { get; set; } AND public Student Student { get; set; } to the Address class (and to be quite frank, I'm trying to understand the difference between what is happening and what you expect to happen!) – Oluwadamilola Adegunwa Apr 06 '20 at 03:26
  • @Damilola Address class is a value object, not an entity – Mohammad Akbari Apr 06 '20 at 03:30

2 Answers2

2

This is a bug introduced by EF Core 3.0 new query processing pipeline, most likely related to the following breaking change Dependent entities sharing the table with the principal are now optional, which is supposed to fix some user requested scenarios, but in fact breaks many others.

It's currently tracked by #18299: Query on owned entity produces overly complicated SQL and unfortunately looks like won't be fixed in 3.1, so the people are expected to wait for 5.0 release. Meanwhile there is nothing you can do about it.

Ivan Stoev
  • 195,425
  • 15
  • 312
  • 343
0

You have mentioned the relation as "Owned" type. Owned type are always included when you are querying the parent object. See here for details.

You can use 'HasOne' relation, to avoid this.

Prakash G. R.
  • 4,746
  • 1
  • 24
  • 35
  • Address property is a value object, that properties are in the Student table and I expected that not join needed for loading value object(Address) properties – Mohammad Akbari Apr 06 '20 at 05:59
  • As mentioned, you should make it as HasOne, and not Owned type. – Prakash G. R. Apr 06 '20 at 06:05
  • based on this https://learn.microsoft.com/en-us/dotnet/architecture/microservices/microservice-ddd-cqrs-patterns/implement-value-objects OwnsOne is correct! – Mohammad Akbari Apr 06 '20 at 07:10