Reputation: 3153
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?
Upvotes: 8
Views: 3750
Reputation: 2018
Since EF 6.0 you can use the native attribute for precision and scale!
public class Blog
{
public int BlogId { get; set; }
[Precision(14, 2)]
public decimal Score { get; set; }
[Precision(3)]
public DateTime LastUpdated { get; set; }
}
If someone else is still interested in the EF Core equivalent code for custom attributes, here's an implementation for EF Core 6+
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class DecimalPrecisionAttribute(Byte precision, Byte scale) : Attribute
{
public Byte Precision { get; set; } = precision;
public Byte Scale { get; set; } = scale;
}
public class DecimalPrecisionAttributeConvention(ProviderConventionSetBuilderDependencies dependencies) :
PropertyAttributeConventionBase<DecimalPrecisionAttribute>(dependencies)
{
protected override void ProcessPropertyAdded(
IConventionPropertyBuilder propertyBuilder,
DecimalPrecisionAttribute attribute,
MemberInfo clrMember,
IConventionContext context)
{
propertyBuilder.HasPrecision(attribute.Precision);
propertyBuilder.HasScale(attribute.Scale);
}
}
internal class DbContext : Microsoft.EntityFrameworkCore.DbContext
{
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
configurationBuilder.Conventions.Add(sp => new DecimalPrecisionAttributeConvention(
sp.GetRequiredService<ProviderConventionSetBuilderDependencies>()));
}
Prior to EF Core 6 since 1.0 you can add the convention with the help of an IDbContextOptionsExtension which is described in this post.
Upvotes: 1
Reputation: 325
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.
Upvotes: 3