2

I have entity that contain value-object with name IdCard like this:

public class UserProfile : BaseEntity<long>
{
  public byte Gender { get; private set; } = 0;

  public int? BirthDate { get; private set; }

  public IdCard IdCard { get; set; }
}

And IdCard member like this:

public class IdCard : ValueObject
    {
        public int? Type { get; set; }

        public string No { get; set; }
    }

I need to make IdCard No as index by using EF fluent api

some thing like this

builder.HasIndex(c => c.IdCard.No);
sos5020
  • 379
  • 1
  • 3
  • 10
  • Using the same classes for either your domain layer and your persistence layer (EF) is the wrong way. It will force you to make comprises about the encapsulation of your domain classes for persistence purposes. Separating your domain classes from your persistence classes is the first step to DDD. – Maxime Gélinas Nov 20 '21 at 07:55

2 Answers2

2

Take a look at the Implement value objects from Microsoft and Using Value Objects with Entity Framework Core links. These two are helpful.

You can create UserProfileConfiguration class as follow:

public class UserProfileConfiguration : IEntityTypeConfiguration<UserProfile>
{
    public void Configure(EntityTypeBuilder<UserProfile> builder)
    {
        builder.HasKey(x => x.Id);
        builder.OwnsOne(x => x.IdCard);
        builder.HasIndex(x => x.No);
    }
}

And then, apply it in OnModelCreating method in your DbContext:

modelBuilder.ApplyConfiguration(new UserProfileConfiguration());
mohabbati
  • 1,162
  • 1
  • 13
  • 31
  • Ardalas solution is like this `builder.OwnsOne(p => p.AnimalType, p =>{p.Property(pp=>pp.Breed).HasColumnName("AnimalType_Breed").HasMaxLength(50);p.Property(pp =>pp.Species).HasColumnName("AnimalType_Species").HasMaxLength(50);});` so we can make the key in the same way. – sos5020 Nov 20 '21 at 11:59
  • 1
    This is another override of `OwnsOne()` method to config your entity. Both of them do the same. In that case, you can use `buildAction` parameter as follow: `builder.OwnsOne(x => x.Shipper, buildAction => { buildAction.HasIndex(x => x.Code); });` – mohabbati Nov 20 '21 at 14:08
0

Ad constructor to Your IdCard that will take single parameter, in this case the No. Following should work for when the value-object have a single field that will be stored in database, so the Type would not have to be stored, but calculated after db fetch. Otherwise this would have to be stored as owned entity. Alternatively you could store Type and No together in single field and provide custom converter for those fields, but this solution would be suboptimal to say the least.

In fluent configuration, assuming that try:

modelBuilder.Entity<UserProfile>(builder =>
{
    builder.HasIndex(c => c.IdCard);
    builder.Property(p => p.IdCard)
        .HasConversion(id => id.No, value => new IdCard(value)); 
});

I do not remember if this will work in EF core without problems - there were some issues when it comes to converters used for primary keys in the past, but its worth a try.

quain
  • 861
  • 5
  • 18
  • This code `builder.HasIndex(c => c.IdCard);` will not working as will because it's not single Property. – sos5020 Nov 18 '21 at 20:56
  • I fixed it with using `OwnsOne(x => x.IdCard)` and I setting my Property type successfully. – sos5020 Nov 18 '21 at 21:01
  • I can fix the index in the same way but I am not sure is it the best way to dealing with Value-Object. – sos5020 Nov 18 '21 at 21:03
  • For value objects it is a preferred way - in this case it is a part of `UserProfile`, it should not be accessible on its own (owned type cannot be accessed through dbSet) and it should be always loaded with it's parent. – quain Nov 18 '21 at 21:21