Search code examples
.netentity-framework-core

Choosing the Best Approach for Computed Columns


This code works as expected: the ExhibitionName property brings the concatenation of the fields according to their values.

public partial class Client
{
    public int Id { get; set; }
    public int Reference { get; set; }

    [StringLength(100)]
    public string Name{ get; set; } = null!;

    [StringLength(50)]
    public string? Nickname{ get; set; }

    public string ExhibitionName => (!String.IsNullOrWhiteSpace(Nickname) ? Nickname: Name) + " (" + Reference+ ")";
    
    public DateOnly Added { get; set; }
    // other properties
}

// db is the DbContext instance for Client.
db.Clients.Select(c => new { c.Id, c.ExhibitionName }).ToList();

However, the SQL statement generated by Entity Framework returns all the columns from the table, even the ones not related, which is not desirable.

The second option would be to use HasComputedColumnSql in the model mapping and achieve the same result via the database.

Is there a third option?


Solution

  • That is, because with your current implementation, the ExhibitionName is not mapped into column in your database. The output looks like:

    enter image description here

    SOLUTION

    One of the solutions as you mentioned is to use HasComputedColumnSql or HasValueGenerator

    To apply that, simply change your property into:

    [JsonIgnore]
    public string ExhibitionName { get; set; }
    

    Then inside the OnModelCreating method add:

    modelBuilder.Entity<Client>().Property(x => x.ExhibitionName)
        .HasValueGenerator<ExhibitionNameGenerator>();
    

    The last step is to create a generator, the sample implementation can looks like this:

    public class ExhibitionNameGenerator : ValueGenerator<string>
    {
        public override string Next(EntityEntry entry)
        {
            if(entry.Entity is Client clientEntity)
            {
                return (!String.IsNullOrWhiteSpace(clientEntity.Nickname) ? clientEntity.Nickname : clientEntity.Name) + " (" + clientEntity.Reference + ")";
            }
    
            // You need to handle this case here
            return "";
        }
    
        public override bool GeneratesTemporaryValues => false;
    }