AutoCorrect Event

Summary

The AutoCorrect event automatically transforms or normalizes a property value before validation occurs. Use it for formatting rules such as uppercasing codes, trimming whitespace, or rounding numbers to the correct precision.

When does it fire?

AutoCorrect fires during the set pipeline, after the read-only check and before validation:

Set property → ReadOnly check → AutoCorrect → Validate → Dirty dependents → Changed

Data type–level AutoCorrect runs first, then property-level AutoCorrect.

Syntax

Property-level syntax:

product.Sku.AutoCorrect()
    .Transform(value => value?.ToUpperInvariant());

Entity-level syntax:

product.AutoCorrect(p => p.Sku)
    .Transform(value => value?.ToUpperInvariant());

Fluent API

Step Method Description
Required .Transform(expression) The function that transforms and returns the corrected value

Transform overloads

Signature Description
.Transform(value => newValue) Transform the value only
.Transform((value, args) => newValue) Transform with access to the entity and services via args

Scenarios

1. Uppercasing a product SKU

Ensure all product SKUs are stored in uppercase regardless of how the user types them.

[Logic]
public class ProductBL(Product.Logic product)
{
    [RegisterLogic]
    public void AutoCorrectSku()
    {
        product.AutoCorrect(p => p.Sku)
            .Transform(sku => sku?.ToUpperInvariant());
    }
}

2. Rounding a currency value to the correct precision

Round the product cost to the configured number of decimal places.

[Logic]
public class ProductBL(Product.Logic product)
{
    [RegisterLogic]
    public void AutoCorrectCost()
    {
        product.AutoCorrect(p => p.Cost)
            .Transform((cost, args) =>
                Math.Round(cost, args.Entity.UnitPriceDecimals, MidpointRounding.AwayFromZero));
    }
}

3. Auto-incrementing a duplicate document number

If a document number already exists, automatically append a suffix to make it unique.

[Logic]
public class SalesOrderBL(SalesOrder.Logic salesOrder)
{
    [RegisterLogic]
    public void AutoCorrectDocNumber()
    {
        salesOrder.AutoCorrect(o => o.DocNumber)
            .Transform((number, args) =>
            {
                string original = number;
                int suffix = 1;
                while (args.GetEntities<SalesOrder>()
                           .Any(o => o.DocNumber == number && o.Guid != args.Entity.Guid))
                {
                    number = $"{original}-{suffix}";
                    suffix++;
                }
                return number;
            });
    }
}

4. Trimming whitespace from a customer ID

Customer IDs should not have leading or trailing whitespace.

[Logic]
public class CustomerBL(Customer.Logic customer)
{
    [RegisterLogic]
    public void AutoCorrectId()
    {
        customer.AutoCorrect(c => c.Id)
            .Transform(id => id?.Trim());
    }
}

5. Normalizing a phone number

Strip non-digit characters so phone numbers are stored in a consistent format.

[Logic]
public class ContactBL(Contact.Logic contact)
{
    [RegisterLogic]
    public void AutoCorrectPhone()
    {
        contact.AutoCorrect(c => c.Phone)
            .Transform(phone =>
            {
                if (string.IsNullOrEmpty(phone)) return phone;
                return new string(phone.Where(char.IsDigit).ToArray());
            });
    }
}

AutoCorrect vs. Validate

AutoCorrect Validate
Purpose Fix the value silently Reject invalid values with an error
When Before validation After auto-correct
Result Returns a transformed value Returns accept/reject with a message
User sees The corrected value An error message

Notes

  • AutoCorrect should never reject a value — it only transforms. Use Validate for rejection.
  • Data type–level AutoCorrect (defined on custom data types) runs before property-level AutoCorrect.
  • AutoCorrect should be idempotent — running it twice on the same value should produce the same result.