efcore выдает ошибку при чтении с базы данных The entity type 'Phone' requires a primary key to be defined. на поле ValueObject

в приложении есть 2 контекста(на чтение и на запись)

public class AppReadDbContext : DbContext
{
    private readonly IConfiguration _configuration;

    public AppReadDbContext(IConfiguration configuration)
    {
        _configuration = configuration;
    }

    public DbSet<ArticleDto> Articles => Set<ArticleDto>();

    public DbSet<AppUserDto> Users => Set<AppUserDto>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(UserEntityConfiguration).Assembly, 
            type => type.FullName.Contains("EntityConfigurations.Read"));
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseNpgsql(_configuration.GetConnectionString("DatabaseAccess"));
        optionsBuilder.UseSnakeCaseNamingConvention();
        optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);
        optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
    }
}
public class AppWriteDbContext : DbContext
{
    private readonly IConfiguration _configuration;

    public AppWriteDbContext(IConfiguration configuration)
    {
        _configuration = configuration;
    }
    public DbSet<Article> Articles => Set<Article>();
    public DbSet<AppUser> Users => Set<AppUser>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(ArticleEntityConfiguration).Assembly,
            type => type.FullName.Contains("EntityConfigurations.Write")); ;
        base.OnModelCreating(modelBuilder);
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseNpgsql(_configuration.GetConnectionString("DatabaseAccess"));
        optionsBuilder.UseSnakeCaseNamingConvention();
        optionsBuilder.LogTo(Console.WriteLine, LogLevel.Information);
    }
}

так же есть две доменные модели(на чтение и на запись)

public class AppUser
{
    private static readonly DateTime minDate = new DateTime(1920, 1, 1);
    private static readonly DateTime maxDate = DateTime.UtcNow;

    private AppUser() { }

    private AppUser(
        string userName,
        string passwordHash,
        string email,
        DateTimeOffset registrationDate)
    {
        UserName = userName;
        PasswordHash = passwordHash;
        Email = email;
        RegistrationDate = registrationDate;
    }

    public Guid Id { get; private set; }
    [Required]
    public string UserName { get; private set; }
    [Required]
    public string PasswordHash { get; private set; }
    public string Email { get; private set; }

    public Phone Phone { get; private set; } 
    public string FirstName { get; private set; } = string.Empty;
    public string LastName { get; private set; } = string.Empty;
    public string SecondName { get; private set; } = string.Empty;
    public DateTimeOffset BirthDate { get; private set; } = DateTimeOffset.MinValue;
    public DateTimeOffset RegistrationDate { get; private set; }


    private IReadOnlyList<Article> _articles = [];
    public IReadOnlyList<Article> Articles => _articles;


    private IReadOnlyList<Comment> _comments = [];
    public IReadOnlyList<Comment> Comments => _comments;


    public static Result<AppUser, Error> Create(
        string userName,
        string passwordHash,
        string emailInput)
    {
        emailInput = emailInput.Trim().ToLower();
        var emailResult = EmailObject.Create(emailInput);
        if (emailResult.IsFailure) return emailResult.Error;

        userName = userName.Trim().ToLower();
        if (string.IsNullOrEmpty(userName))
            userName = emailResult.Value.Email;

        passwordHash = passwordHash.Trim();
        if (string.IsNullOrEmpty(passwordHash))
            return Errors.General.InValid(passwordHash);

        return new AppUser(
            userName,
            passwordHash,
            emailResult.Value.Email,
            DateTimeOffset.UtcNow);
    }
}
public class AppUserDto
{
    public Guid Id { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }

    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string SecondName { get; set; }
    public DateTimeOffset BirthDate { get; set; }
    public DateTimeOffset RegistrationDate { get; set; }

    public ICollection<ArticleDto> Articles { get; set; }
    public ICollection<CommentDto> Comments { get; set; }
}

в доменной модели есть value object Phone

public class Phone
{
    private const string regexValidationPhone = @"^((8 |\+7)[\- ]?)?(\(?\d{3}\)?[\- ]?)?[\d\- ]{7,10}$";

    private Phone() { }

    private Phone(string phoneNumber)
    {
        PhoneNumber = phoneNumber;
    }
    public Guid Id { get;}
    public string PhoneNumber { get;} = string.Empty;

    public static Result<Phone, Error> Create(string input)
    {
        if(string.IsNullOrEmpty(input.Trim()))
            return Errors.General.InValid(input);
        if(!Regex.IsMatch(input, regexValidationPhone))
            return Errors.General.InValid(input);
        return new Phone(input);

    }
}

определены конфигурации для обоих моделей

public class AppUserEntityConfiguration : IEntityTypeConfiguration<AppUser>
{
    public void Configure(EntityTypeBuilder<AppUser> builder)
    {
        builder.ToTable("users");

        builder.HasKey(u => u.Id);

        builder.HasIndex(u => u.UserName)
            .IsUnique();
        builder.HasIndex(u => u.Email)
            .IsUnique();

        builder.Property(u => u.Id)
            .HasColumnName("id");
        builder.Property(u => u.UserName)
            .IsRequired()
            .HasColumnName("user_name");
        builder.Property(u => u.PasswordHash)
            .IsRequired()
            .HasColumnName("user_password_hash");
        builder.Property(p => p.Email)
                    .HasColumnName("email")
                    .IsRequired();
        builder.Property(u => u.RegistrationDate)
            .IsRequired()
            .HasColumnName("user_registration_date;");
        builder.Property(u => u.FirstName)
            .HasDefaultValue("")
            .IsRequired()
            .HasColumnName("user_first_name");
        builder.Property(u => u.SecondName)
            .HasDefaultValue("")
            .IsRequired()
            .HasColumnName("user_second_name;");
        builder.Property(u => u.LastName)
            .HasDefaultValue("")
            .IsRequired()
            .HasColumnName("user_last_name;");
        builder.Property(u => u.BirthDate)
            .IsRequired()
            .HasColumnName("user_birth_date;")
            .HasDefaultValue(DateTimeOffset.MinValue);

        builder.OwnsOne(u => u.Phone, p =>
                p.Property(p => p.PhoneNumber)
                    .HasDefaultValue("+79998887766")
                    .HasColumnName("phone_number"));


        builder.HasMany(u => u.Articles)
            .WithOne(a => a.Author)
            .OnDelete(DeleteBehavior.NoAction);

        builder.HasMany(u => u.Comments)
            .WithOne(c => c.Author)
            .OnDelete(DeleteBehavior.NoAction);
    }
}
public class UserEntityConfiguration : IEntityTypeConfiguration<AppUserDto>
{
    public void Configure(EntityTypeBuilder<AppUserDto> builder)
    {
        builder.ToTable("users");

        builder.HasKey(u => u.Id);

        builder.HasIndex(u => u.UserName)
            .IsUnique();
        builder.HasIndex(u => u.Email)
            .IsUnique();

        builder.Property(u => u.Id)
            .HasColumnName("id");
        builder.Property(u => u.UserName)
            .HasColumnName("user_name");
        builder.Property(p => p.Email)
            .HasColumnName("email");
        builder.Property(u => u.RegistrationDate)
            .HasColumnName("user_registration_date;");
        builder.Property(u => u.FirstName)
            .HasColumnName("user_first_name");
        builder.Property(u => u.SecondName)
            .HasColumnName("user_second_name;");
        builder.Property(u => u.LastName)
            .HasColumnName("user_last_name;");
        builder.Property(u => u.BirthDate)
            .HasColumnName("user_birth_date;");

        builder.Property(u => u.Phone)
            .HasColumnName("phone_number");

        builder.HasMany(u => u.Articles)
            .WithOne(a => a.Author);

        builder.HasMany(u => u.Comments)
            .WithOne(c => c.Author);
    }
}

вроде должно все работать. на запись так и есть все добавляется в базу без проблем. а вот на чтение когда пытаюсь получить AppUserDto EF Core выдает ошибку:InvalidOperationException The entity type 'Phone' requires a primary key to be defined. If you intended to use a keyless entity type, call 'HasNoKey' in 'OnModelCreating'. For more information on keyless entity types причем если меняю на стринг в AppUser поле Phone все работает хорошо.


Ответы (0 шт):