When creating or editing an entity property of type decimal in ABP Suite's UI, there's no field to specify precision and scale (e.g. 18,6). The generated DbContext.OnModelCreating code and migration only use the default decimal mapping.
Because of this, every time I:
add a new entity with a decimal property, or add a new decimal property to an existing entity, or regenerate/modify any other property on a table that already has decimal columns, I have to manually go into the generated OnModelCreating and add .HasColumnType("decimal(18,6)") (or the appropriate precision) to each decimal Property() call by hand, then create a new EF Core migration just for that fix — every single time, since Suite doesn't remember or apply it.
Question: Is there a way to configure precision/scale for decimal properties directly in ABP Suite (globally or per-property), so it's included automatically in the generated code and migration, instead of manually patching OnModelCreating after every generation?
Steps to reproduce:
Open ABP Suite → create or edit an entity Add/keep a property of type decimal No precision/scale option is available in the property editor Generate → resulting OnModelCreating/migration has no explicit precision, requiring manual .HasColumnType(...) fix and an extra migration
5 Answer(s)
-
0
- ABP Suite’s current entity/property designer does not provide a UI (per-property or global) to set decimal precision/scale and persist that into the generated EF Core mappings/migrations. So, what you’re seeing (default provider mapping, then having to manually add
HasColumnType("decimal(18,6)")/HasPrecision(18, 6)and create a follow-up migration) is expected with the current Suite generation capabilities. - As a workaround that does stick and doesn’t require re-applying after every Suite generation, configure the decimal mapping in your own EF Core model configuration (outside the generated/regenerated parts) and keep it there:
- Create a custom
IEntityTypeConfiguration<TEntity>(or ABP-style model builder extension) for each entity (or a base convention) and set precision/scale there. - Ensure Suite doesn’t overwrite that file (i.e., keep it in a place Suite doesn’t regenerate).
- Prefer EF Core’s
HasPrecision(precision, scale)(provider-agnostic) orHasColumnType(...)if you must target a specific DB (e.g., SQL Server).
- Create a custom
No verified ABP documentation/support entry was found that states Suite supports decimal precision/scale configuration in the entity property editor, or that there is a global Suite setting for it.
Links (verified):
- https://abp.io/suite
- https://abp.io/docs/latest/studio/overview
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - ABP Suite’s current entity/property designer does not provide a UI (per-property or global) to set decimal precision/scale and persist that into the generated EF Core mappings/migrations. So, what you’re seeing (default provider mapping, then having to manually add
-
0
Thanks for confirming. I've implemented the workaround as suggested — a global EF Core convention that sets decimal(18,6) precision/scale on all decimal properties that don't already have an explicit override, added at the end of OnModelCreating in the Suite-generated DbContext:
foreach (var property in builder.Model.GetEntityTypes() .SelectMany(t => t.GetProperties()) .Where(p => p.ClrType == typeof(decimal) || p.ClrType == typeof(decimal?)) .Where(p => p.GetColumnType() == null)) { property.SetPrecision(18); property.SetScale(6); } My concern: this file ({Service}DbContext.cs) is regenerated/appended to by Suite every time I add a new entity via the designer. You mentioned putting this "outside the generated/regenerated parts" — but this file is exactly the one Suite writes to when generating entities.
Given that, what is the officially recommended safe location for this kind of global convention so it's guaranteed not to be affected by future Suite generations? Specifically:
Is it safe to leave this code at the end of OnModelCreating in the Suite-generated DbContext, given Suite only appends new builder.Entity<...>() blocks and doesn't rewrite the whole file? Or should this go in a separate partial class file, a custom IModelCustomizer, or some other Suite-recommended extension point? Does ABP Suite provide any "custom code" region/marker (similar to the //Write your custom code here... marker in Extended entity files) inside the generated DbContext that is guaranteed to survive regeneration? Appreciate a definitive answer so I don't lose this configuration on the next Suite run.
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hi,
ABP Suite doesn't currently provide precision/scale options for decimal properties. I'll share this with the Suite team as a feature request.
About your questions: Suite updates the existing DbContext file in place instead of regenerating it from a template, so your loop is preserved by the current entity generator — but newly generated model configuration can be added after it, and there is no protected custom-code marker inside the generated DbContext. So I don't recommend relying on a loop at the end of
OnModelCreating.Use EF Core's
ConfigureConventionshook on the same DbContext instead. A partial class or a customIModelCustomizerisn't needed:protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { base.ConfigureConventions(configurationBuilder); configurationBuilder.Properties<decimal>().HavePrecision(18, 6); }Suite's entity generation doesn't add or remove code inside this method, so the override is preserved when entities are regenerated.
This sets
decimal(18,6)as the default for alldecimalanddecimal?properties in this DbContext. Explicit per-propertyHasPrecision(...)orHasColumnType(...)configuration still takes precedence.Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hello, Thank, you, but what if I have multiple decimal types?
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) { base.ConfigureConventions(configurationBuilder); configurationBuilder.Properties<decimal>().HavePrecision(18, 6); } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder);
const string PriceDecimalType = "decimal(18, 6)"; const string CordinateDecimalType = "decimal(18, 10)"; const string DecimalType102 = "decimal(10, 2)"; const string DecimalType182 = "decimal(18, 2)"; builder.ConfigureEventInbox(); builder.ConfigureEventOutbox(); builder.Entity<OutboxJournalEntry>(b => { b.ToTable(DbTablePrefix + "OutboxJournalEntries", DbSchema); b.ConfigureByConvention(); b.Property(x => x.TenantId).HasColumnName(nameof(OutboxJournalEntry.TenantId)); b.Property(x => x.BookingId).HasColumnName(nameof(OutboxJournalEntry.BookingId)); b.Property(x => x.DetailId).HasColumnName(nameof(OutboxJournalEntry.DetailId)); b.Property(x => x.ServiceTypeId).HasColumnName(nameof(OutboxJournalEntry.ServiceTypeId)); b.Property(x => x.Narration).HasColumnName(nameof(OutboxJournalEntry.Narration)).HasMaxLength(OutboxJournalEntryConsts.NarrationMaxLength); b.Property(x => x.BuyingROE).HasColumnName(nameof(OutboxJournalEntry.BuyingROE)); b.Property(x => x.SellingROE).HasColumnName(nameof(OutboxJournalEntry.SellingROE)); b.Property(x => x.IsSameCurrency).HasColumnName(nameof(OutboxJournalEntry.IsSameCurrency)); b.Property(x => x.EventType).HasColumnName(nameof(OutboxJournalEntry.EventType)).HasMaxLength(OutboxJournalEntryConsts.EventTypeMaxLength); b.Property(x => x.RetryCount).HasColumnName(nameof(OutboxJournalEntry.RetryCount)); b.Property(x => x.PublishedAt).HasColumnName(nameof(OutboxJournalEntry.PublishedAt)); b.Property(x => x.Status).HasColumnName(nameof(OutboxJournalEntry.Status)); b.Property(x => x.ServiceTypeName).HasColumnName(nameof(OutboxJournalEntry.ServiceTypeName)).HasMaxLength(OutboxJournalEntryConsts.ServiceTypeNameMaxLength); b.Property(x => x.ReverseSupplierId).HasColumnName(nameof(OutboxJournalEntry.ReverseSupplierId)); b.Property(x => x.UserId).HasColumnName(nameof(OutboxJournalEntry.UserId)); b.Property(x => x.UserTypeId).HasColumnName(nameof(OutboxJournalEntry.UserTypeId)); b.Property(x => x.Amount).HasColumnName(nameof(OutboxJournalEntry.Amount)); b.Property(x => x.TaxTypeCode).HasColumnName(nameof(OutboxJournalEntry.TaxTypeCode)).HasMaxLength(OutboxJournalEntryConsts.TaxTypeCodeMaxLength); b.Property(x => x.HeaderId).HasColumnName(nameof(OutboxJournalEntry.HeaderId)); b.Property(x => x.EntityType).HasColumnName(nameof(OutboxJournalEntry.EntityType)).HasMaxLength(OutboxJournalEntryConsts.EntityTypeMaxLength); b.Property(x => x.CurrencyId).HasColumnName(nameof(OutboxJournalEntry.CurrencyId)); b.Property(x => x.CurrencyCode).HasColumnName(nameof(OutboxJournalEntry.CurrencyCode)).HasMaxLength(OutboxJournalEntryConsts.CurrencyCodeMaxLength); b.Property(x => x.BaseAmount).HasColumnName(nameof(OutboxJournalEntry.BaseAmount)); b.Property(x => x.BookingDate).HasColumnName(nameof(OutboxJournalEntry.BookingDate)); b.Property(x => x.CheckinDate).HasColumnName(nameof(OutboxJournalEntry.CheckinDate)); b.Property(x => x.CheckOutDate).HasColumnName(nameof(OutboxJournalEntry.CheckOutDate)); b.Property(x => x.DeadlineDate).HasColumnName(nameof(OutboxJournalEntry.DeadlineDate)); b.Property(x => x.acc).HasColumnName(nameof(OutboxJournalEntry.acc)); });This is my dbcontext file, and from this check, BaseAmount is 18,6 and BuyingROE is 18,10. but it's removing when I am adding a new column in this entity
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) -
0
Hi,
Manual configuration inside the Suite-generated
builder.Entity<...>(b => { ... })blocks is lost when the entity is regenerated — Suite rebuilds those blocks from the entity definition. Adding a secondbuilder.Entity<OutboxJournalEntry>(...)block for the same entity isn't safe either; it can get removed by the regeneration too.For multiple precision groups, keep the
ConfigureConventionsdefault for your most common one (18, 6) and configure the exceptions by entity type and property name, directly inOnModelCreatingbut outside any Suite-generatedbuilder.Entity<...>block, so it survives regeneration:var decimalOverrides = new Dictionary<(Type EntityType, string PropertyName), (int Precision, int Scale)> { [(typeof(OutboxJournalEntry), nameof(OutboxJournalEntry.BuyingROE))] = (18, 10), [(typeof(OutboxJournalEntry), nameof(OutboxJournalEntry.SellingROE))] = (18, 10), // Add your (10, 2) and (18, 2) properties here in the same way. }; foreach (var entityType in builder.Model.GetEntityTypes()) { var clrType = entityType.ClrType; if (clrType == null) { continue; } foreach (var property in entityType.GetProperties()) { if (decimalOverrides.TryGetValue((clrType, property.Name), out var precisionScale)) { property.SetPrecision(precisionScale.Precision); property.SetScale(precisionScale.Scale); } } }We've also added precision/scale settings for decimal properties to Suite's entity designer. This will be available in ABP Suite 10.7, so you'll be able to set these per property in the UI and remove the override map and loop above.
Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)