Search code examples
c#entity-framework-coreowned-types

Strongly typed id in EF Core while projecting to some model


I try to query Postgresql DB on models with strongly typed ids and project to a model.

Cannot understand why query doesn't work.

It throws this exception:

Unhandled exception. System.InvalidOperationException: Translation of 'EF.Property(u3.Id, "ClientId")' failed. Either the query source is not an entity type, or the specified property does not exist on the entity type.

If I remove the strongly typed IDs from the query and use a plain Guid instead, the query executes correctly and returns data from the database.

public class Program
{
    public static void Main()
    {
        using var context = new EntityContext();

        var query = (from client in context.Clients
            let managers = from managerId in client.ManagerIds
                join user in context.Users on managerId equals user.Id
                select new { user.Id, user.Name }
            select new { client, managers }).ToList();
    }
}

public record UserId(Guid Value);
public record ClientId(Guid Value);

public class Client
{
    public ClientId Id { get; set; }
    public ICollection<UserId> ManagerIds { get; set; } = new List<UserId>();
}

public record User(UserId Id, string Name);

public class EntityContext : DbContext
{
    public DbSet<Client> Clients { get; set; }
    public DbSet<User> Users { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>().HasKey(x => x.Id);
        modelBuilder.Entity<User>()
            .Property(x => x.Id)
            .HasConversion(x => x.Value, x => new UserId(x));

        modelBuilder.Entity<Client>().HasKey(x => x.Id);
        modelBuilder.Entity<Client>()
            .Property(x => x.Id)
            .HasConversion(x => x.Value, x => new ClientId(x));
        modelBuilder.Entity<Client>()
            .OwnsMany(
                client => client.ManagerIds, 
                ownedNavigationBuilder =>
                {
                    ownedNavigationBuilder
                        .WithOwner()
                        .HasForeignKey("ClientId");
                    ownedNavigationBuilder
                        .Property(x => x.Value)
                        .HasColumnName("ManagerId");
                }
            );
    }
}

Solution

  • This relationship doesn't look right:

            modelBuilder.Entity<Client>()
                .OwnsMany(
                    client => client.ManagerIds, 
                    ownedNavigationBuilder =>
                    {
                        ownedNavigationBuilder
                            .WithOwner()
                            .HasForeignKey("ClientId");
                        ownedNavigationBuilder
                            .Property(x => x.Value)
                            .HasColumnName("ManagerId");
                    }
                );
    

    OwnsMany/HasMany are for relationships between entities, so I would expect Client to have an ICollection<User> Managers { get; } = []; Rather than a collection of UserIds. EF would be expecting the record to be an entity not a GUID. I suspect EF can store a list of GUIDs within a Client row, but a properly normalized many-to-many relationship would be better.