Tag: Entity Framework

Entity Framework Decimal Scale and Precision Convention

Entity Framework Decimal Scale and Precision Convention

Entity Framework very much relies on conventions in order to accomplish a lot of what it does. In many cases it can be a hands-off tool: so long as you follow its conventions you should be in the clear. What do you do when the convention just isn’t cutting it?

Entity Framework Rounding to Two Decimal Places

I recently created a table that had a column setup as DECIMAL(19, 4).  That’s a precision of 19 and a scale of 4. Using Entity Framework I created a record in the table where I attempted to utilize the allotted scale (for example, 20.0015). The save was successful, but to my surprise the persisted number was rounded to two decimal places. It turns out this is by design, as Entity framework defaults to saving two decimal places. 

Entity Framework Decimal Scale and Precision Convention

Fortunately there’s a straightforward approach to circumventing this behavior by defining a new convention for Entity Framework to follow when dealing with any given property. Let’s see what that looks like.

Create the Decimal Precision Attribute Class

A new attribute will be created that serves the purpose of defining a precision and scale. This attribute can later be used to decorate a property of an entity object, which we’ll configure EF to recognize.

Add a new class DecimalPrecisionAttribute with the following:

[AttributeUsage(AttributeTargets.Property, Inherited = false, AllowMultiple = false)]
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; }
}

Create the Decimal Precision Attribute Convention Class

The attribute is created, it’s time to make a new decimal convention class which will later be wired into the overall conventions Entity Framework is aware of.

Add a new class DecimalPrecisionAttributeConvention with the following:

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);
    }
}

Add Convention to the Model Builder

Now that we’ve created a new convention to handle our precision and scale needs, we need to add it to the model builder. 

In your DbContext class, override the OnModelCreating method with the following:

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

Decorate a Property with the Decimal Precision Attribute

We’ve created a property to define precision and scale and added a convention to Entity Framework to use this precision and scale when found. Now all we have to do is decorate a property on one of our entity objects. Here’s what that might look like:

public class PayInfo {
    [DecimalPrecision(19, 4)]
    public decimal Rate { get; set; }
}

There you have it: properties decorated with the DecimalPrecision attribute will now persist with the predefined precision and scale.