How to map database column to EF model property in a more efficient way than I'm doing now

1.1k Views Asked by At

I have a nullable varchar(max) column in SQL Server that I'm mapping to a Guid? in EF code-first. However, this property is actually in a base class that many other entities derive from.

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Model1>().Property(e => e.Property1).HasConversion(p => p.ToString(), p => (Guid?)Guid.Parse(p));
}

The above line is repeated many times for each table. Is there a way to tell EF that this is a base class property so the mapping can be declared only once?

3

There are 3 best solutions below

0
On BEST ANSWER

Sure it is possible. With the lack of custom conventions, it is achieved with the "typical" modelBuilder.Model.GetEntityTypes() loop. Something like this (just change the base class and the property names):

var entityTypes = modelBuilder.Model.GetEntityTypes()
    .Where(t => t.ClrType.IsSubclassOf(typeof(BaseClass)));

var valueConverter = new ValueConverter<Guid, string>(
    v => v.ToString(), v => (Guid?)Guid.Parse(v));

foreach (var entityType in entityTypes)
    entityType.FindProperty(nameof(BaseClass.Property1)).SetValueConverter(valueConverter);

You may also consider using the EF Core provided out of the box Guid to String converter:

var valueConverter = new GuidToStringConverter();
0
On

Better to make next calculation property:

[Column("Property1")]
public string Property1Raw { get; set; }

[IgnoreDataMember]
public Guid? Property1
{
    get => Guid.TryParse(Property1AsString, out Guid result) ? result : (Guid?)null;
    set => Property1Raw = value?.ToString();
}
0
On

Another way to do is it to have a matching base class IEntityTypeConfiguration:

internal class EntityConfiguration<T> : IEntityTypeConfiguration<T> where T : Entity
{
    public virtual void Configure(EntityTypeBuilder<T> builder)
    {
        builder.Property(e => e.Property1).HasConversion(p => p.ToString(), p => (Guid?)Guid.Parse(p));
        // ... Other base-specific config here
    }
}

(Assuming here your base class is called Entity - change as needed).

This works better when you use the pattern of factoring out your entity configurations, so yours might be like this:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.ApplyConfiguration(new Model1EntityConfiguration());
    modelBuilder.ApplyConfiguration(new Model2EntityConfiguration());
    // ...
}

...

internal sealed class Model1EntityConfiguration : EntityConfiguration<Model1>
{
    public override void Configure(EntityTypeBuilder<Model1> builder)
    {
        base.Configure(builder); // <-- here's the key bit
        // ...; e.g.
        builder.Property(c => c.Name).HasMaxLength(80).IsRequired();
    }
}