Entity Framework 7 Set decimal precision for model builder

entity-framework-core

Question

I have been trying to figure out how to set the decimal precision for EF7 (Beta 4) with no luck.

I was expecting to do something like:

modelBuilder.Entity<SomeClass>().Property(p => p.DecimalProperty).Precision(10, 6)

This does not appear to be available, but I was able to find the following class in the repository in GitHub:

https://github.com/aspnet/EntityFramework/blob/7.0.0-beta4/src/EntityFramework.Relational/RelationalDecimalTypeMapping.cs

There are no examples of using the RelationalTypeMapping classes or method signatures with them. Maybe this is just used as part of the mapping api for retrieving information?

Another place I might expect this to be is the following:

modelBuilder.Entity<SomeClass>().Property(p => p.DecimalProperty).ForRelational().ColumnType() 

or

modelBuilder.Entity<SomeClass>().Property(p => p.DecimalProperty).ForSqlServer().ColumnType()

These only takes a string, is this functionality just not implemented yet or am I just not looking in the correct place?

Edit: Just realized that string is probably for .ColumnType("decimal(10,6)") type of solution until this is built out further, still wouldn't mind getting some clarification though as I would prefer not to use strings for this

Edit: after clarification from bricelam I ended up creating the following extension to use for now to avoid using the string, and I appreciate the simplicity of their approach:

public static RelationalPropertyBuilder DecimalPrecision(this RelationalPropertyBuilder propertyBuilder, int precision, int scale)
    {
        return propertyBuilder.ColumnType($"decimal({precision},{scale})");
    }

Usage example:

modelBuilder.Entity<SomeClass>().Property(p => p.DecimalProperty).ForRelational().DecimalPrecision(10,6);

Edit: Making modification for RC1

I haven't tested these out yet, but I just threw together the following 2 samples of what this will probably look like with RC1

    public static PropertyBuilder DecimalPrecision(this PropertyBuilder propertyBuilder, string precision, string scale)
    {
        return propertyBuilder.HasColumnType($"decimal({precision},{scale})");
    }

    public static PropertyBuilder SqlDecimalPrecision(this PropertyBuilder propertyBuilder, string precision, string scale)
    {
        return propertyBuilder.ForSqlServerHasColumnType($"decimal({precision},{scale})");
    }

Since I have not yet tried this I am not sure which would be the correct usage between "HasColumnType" or "ForSqlServerHasColumnType", but hopefully this will point someone in the right direction.

Accepted Answer

Your workaround is the design we intended. Instead of having a bunch of "facets" you can set on a type like precision, scale, max length, unicode/ansi, fixed/variable length, etc. We decided to keep it simple: If the default type mapping isn't what you want, tell us what type to use. There have been talks of going back on this decision and reintroducing the "facets". If you feel strongly about it, I would encourage you to create a new issue.

Also note that there are a bunch of other bugs in type mapping right now, but they should be fixed by the time we release beta5.


Popular Answer

The example shown seems to be outdated as per EF RC1.

Here is how I set precision on a decimal field.

Say I have an entity

public class Review
{
    public int ReviewId { get; set; }
    public decimal TotalScore { get; set; } //I want a precision field in DB
    public DateTime CreatedOn { get; set; }
    [Timestamp]
    public byte[] RowVersion { get; set; }
}

then in my context class, on model creating, I instantiate the mapping (I could do the mapping there, but I like to keep it separated)

public class MyDbContext : DbContext
{
    public MyDbContext(DbContextOptions<MyDbContext> options ) : base(options)
    {
    }

    public DbSet<Review> Reviews { get; set; }
    //etc.

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

        //Mappings
        new ReviewMap(modelBuilder.Entity<Review>());
        //etc..
    }
}

and then the mapping. Remember to use the namespace where the Model extensions are:

using Microsoft.Data.Entity; //here is where the extensions are
public class ReviewMap
{
    public ReviewMap(EntityTypeBuilder<Review> entityBuilder)
    {
        entityBuilder.HasKey(r => r.ReviewId);

        //Using the column type extension
        entityBuilder.Property(r => r.TotalScore)
            .HasColumnType($"decimal(5,2)")
            .IsRequired(true);

        //and this has nothing to do with the example but it's interesting
        //to show how to use Sql command to automatically fulfil a value 
        //when adding a new Entity
        entityBuilder.Property(r => r.CreatedOn)
            .ValueGeneratedOnAdd()
            .HasDefaultValueSql("GETUTCDATE()")
            .IsRequired(true);
    }
}


Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why