Vogen icon indicating copy to clipboard operation
Vogen copied to clipboard

VogenEfCoreConverters is not adding EfCoreConverter to entities

Open Cordelia242 opened this issue 3 months ago • 1 comments

Describe the bug

I'm currently working using an onion architecture and entity framework on .net 9. I have declared my aggregate entity and value objects (vogen) on the domain layer, and the VogenEfCoreConverters on the infrastructure layer and referenced the value objects, but When I go to the entity configuration, I cant use the .HasConversion(new UserDisplayName.EfCoreValueConverter()) because the method doesnt exists. I also tried to use the hasVogenConversion() which is generated but produces this error when creating/getting an entity: System.InvalidCastException: Invalid cast from 'System.String' to 'Core.UserAggregate.Structures.UserEmail'.

Steps to reproduce

  1. Create VO on the domain layer:
[ValueObject<string>]
[Instance("Unspecified", "")]
public partial class UserDisplayName
{
  private static Validation Validate(string input)
  {
    if (string.IsNullOrWhiteSpace(input))
      return Validation.Invalid("Display name cannot be empty.");
    if (input.Length > 64)
      return Validation.Invalid("Display name cannot be longer than 64 characters.");
    if (!Regex.IsMatch(input, @"^\p{L}+$"))
      return Validation.Invalid("Display name contains invalid characters.");

    return Validation.Ok;
  }

  private static string NormalizeInput(string input) =>
    CultureInfo.CurrentCulture.TextInfo.ToTitleCase(Regex.Replace(input.Trim(), @"\s+", " "));
}
  1. Added to the Aggregate Entity
public class User : EntityBase<Guid>, IAggregateRoot
{
  public UserEmail Email { get; private set; }
  public string Password { get; private set; }
  public UserDisplayName DisplayName { get; private set; }
  public DateTime CreatedAt { get; private set; }

  public User(UserEmail email, string password, UserDisplayName displayName)
  {
    Id = Guid.NewGuid();
    Email = email;
    Password = password;
    DisplayName = displayName;
    CreatedAt = DateTime.Now;
  }

  protected User()
  {
    Id = Guid.NewGuid();
    Email = UserEmail.Unspecified;
    Password = string.Empty;
    DisplayName = UserDisplayName.Unspecified;
  } // For EF Core
}
  1. Add the partial Converter on infra layer:
[EfCoreConverter<UserEmail>]
[EfCoreConverter<UserDisplayName>]
public partial class VogenEfCoreConverters;
  1. Add the EF configuration
public class UserConfiguration : IEntityTypeConfiguration<User>
{
  public void Configure(EntityTypeBuilder<User> builder)
  {
    builder.Property(u => u.Email).HasVogenConversion().IsRequired();

    builder.Property(u => u.Password).IsRequired();

    builder
      .Property(u => u.DisplayName)
      .HasVogenConversion()
      .IsRequired()
      .HasMaxLength(DataSchemaConstants.DEFAULT_NAME_LENGTH);
    builder.Property(u => u.CreatedAt).IsRequired();
  }
}

Expected behaviour

The VO should own its EF converter when using the VogenEfCoreConverters with decorators

Cordelia242 avatar Sep 10 '25 03:09 Cordelia242

Thanks for the bug report @Cordelia242 - I'll hopefully get around to this soon!

SteveDunn avatar Nov 24 '25 08:11 SteveDunn