Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding an attribute to configure Decimal precision in Entity Framework Core

I had the following attribute property for my EF 6 .Net Framework app:

[AttributeUsage(AttributeTargets.Property)]          
public sealed class DecimalPrecisionAttribute : Attribute
{              
    public DecimalPrecisionAttribute(Byte precision, Byte scale)
    {
        Precision = precision;
        Scale = scale;
    }

    public Byte Precision { get; set; }
    public Byte Scale { get; set; }
}

public class DecimalPrecisionAttributeConvention
    : PrimitivePropertyAttributeConfigurationConvention<DecimalPrecisionAttribute>
{
    public override void Apply(
        ConventionPrimitivePropertyConfiguration configuration,
        DecimalPrecisionAttribute attribute)
    {
        if (attribute.Precision < 1 || attribute.Precision > 38)
        {
            throw new InvalidOperationException("Precision must be between 1 and 38.");
        }

        if (attribute.Scale > attribute.Precision)
        {
            throw new InvalidOperationException(
                "Scale must be between 0 and the Precision value.");
        }

        configuration.HasPrecision(attribute.Precision, attribute.Scale);
    }
}

Then in my DbContext:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Add(new DecimalPrecisionAttributeConvention());
        base.OnModelCreating(modelBuilder);
    }

This allowed me to set the precision on properties on my POCO's:

[DecimalPrecision(5,2)]
public Decimal WeirdNumber { get; set; }

But I am having trouble puzzling out how to do this for EF Core.

Is there an equivalent in .Core?

I have comme up with the following:

public static class DecimalPrecisionAttributeConvention
{
    public static void ApplyToTable(IMutableEntityType table)
    {
        foreach (var col in table.GetProperties())
        {
            var attribute = col
                ?.PropertyInfo
                ?.GetCustomAttributes<DecimalPrecisionAttribute>()
                ?.FirstOrDefault();

            if (attribute == null)
            {
                continue;
            }

            if (attribute.Precision < 1 || attribute.Precision > 38)
            {
                throw new InvalidOperationException("Precision must be between 1 and 38.");
            }

            if (attribute.Scale > attribute.Precision)
            {
                throw new InvalidOperationException(
                    "Scale must be between 0 and the Precision value.");
            }

            col.Relational().ColumnType =
                $"decimal({attribute.Precision}, {attribute.Scale})";
        }
    }
}

And thin my my on model create:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        foreach (var table in modelBuilder.Model.GetEntityTypes())
        {
            DecimalPrecisionAttributeConvention.ApplyToTable(table);
        }
    }

Is the the best way to accomplish this?

like image 908
sheamus Avatar asked Oct 30 '22 00:10

sheamus


1 Answers

For EntityFrameworkCore, I use this solution. In your DbContext, override OnModelCreating method:

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            // Set default precision to decimal property
            foreach (var property in modelBuilder.Model.GetEntityTypes()
                .SelectMany(t => t.GetProperties())
                .Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?)))
            {
                property.SetColumnType("decimal(18, 2)");
            }
        }

Hope this helps.

like image 61
Michele Caggiano Avatar answered Nov 15 '22 06:11

Michele Caggiano