Properties

Properties define the data fields on an entity. Each property requires a [Property<T>] attribute that specifies the data type and a display label.

[Property<T>("Label")]

The generic type parameter T is a data type that controls validation, formatting, and database column type. The string argument is the human-readable label.

[Property<DataTypes.Text>("Name")]
public partial string Name { get; set; }

Properties must be partial — the source generator provides the implementation with change tracking and event hooks.

Optional Parameters

Parameter Type Description
Description string? User-facing description explaining the property's purpose
Technical string? Internal notes for developers (not shown to end users)
[Property<DataTypes.ProperNoun>("Full name",
    Technical = "Cached property from PrimaryContact.FullName so that we do a full-text search.")]
public partial string FullName { get; }

Common Data Types

Data Type CLR Type Example
DataTypes.Text string Names, short text (max 100)
DataTypes.IdText string IDs, SKUs — uppercase, barcode-safe (max 15)
DataTypes.MultilineText string Long descriptions, notes (unlimited)
DataTypes.ProperNoun string People/business names — auto title-cased (max 50)
DataTypes.Integer int Whole numbers
DataTypes.Decimal decimal Numbers with 2 decimal places
DataTypes.Currency decimal Money values (2 decimals)
DataTypes.Boolean bool True/false flags
DataTypes.DateOnly DateOnly? Dates without time
DataTypes.DateTime DateTime? Date and time
DataTypes.Enum TEnum Any C# enum

See Data Types for the complete list.

[Required]

Marks a property as non-nullable. The value must be provided before saving.

[Required]
[Property<DataTypes.IdText>("SKU")]
public partial string Sku { get; set; }

[MaxLength(n)]

Overrides the data type's default maximum length. Only applies to string properties.

[MaxLength(20)]
[Property<DataTypes.IdText>("SKU")]
public partial string Sku { get; set; }

If omitted, the data type's default max length is used (e.g., IdText defaults to 15, Text defaults to 100).

[DefaultValue(...)]

Sets the initial value when a new entity is created. Uses System.ComponentModel.DefaultValueAttribute.

using System.ComponentModel;

[Property<DataTypes.Text>("Color")]
[DefaultValue("#ffffff")]
public partial string Color { get; set; }

[Property<DataTypes.Enum>("Status")]
[DefaultValue(SalesOrderStatus.Open)]
public partial SalesOrderStatus Status { get; set; }

[DefaultValue(1)]
[Property<DataTypes.PositiveDecimal>("Factor")]
public partial decimal Factor { get; set; }

Read-Only Properties

Use { get; } (no setter) for computed properties whose value is set by business logic:

[Property<DataTypes.ProperNoun>("Full name")]
public partial string FullName { get; }

[Virtual]
[Property<DataTypes.Currency>("Total Price")]
public partial decimal TotalPrice { get; }

Read-only properties are typically populated by Compute events in business logic.

[Virtual] on Properties

Marks a property as computed — it is not persisted to the database. The value is calculated at runtime by business logic.

[Virtual]
[Property<DataTypes.Currency>("Subtotal")]
public partial decimal Subtotal { get; }

[Virtual]
[Property<DataTypes.Currency>("Total")]
public partial decimal Total { get; }

Virtual properties:

  • Have no database column
  • Are recalculated when their dependencies change
  • Are typically read-only ({ get; })
  • Can also be read-write ({ get; set; }) for user-editable computed values
// Virtual but editable — user can override the computed value
[Virtual]
[Property<DataTypes.Decimal>("Quantity")]
public partial decimal QuantityUom { get; set; }

Custom Data Types on Properties

You can use domain-specific data types defined in your project (see Creating Data Types):

[Property<ProductUnitPrice>("Cost",
    Description = "The cost of the product")]
public partial decimal Cost { get; set; }

[Property<ProductQuantity>("Quantity (Each)")]
public partial decimal Quantity { get; set; }

Complete Example

using System.ComponentModel;

namespace Benevia.ERP.Model.Products;

[ApiEntity]
[NaturalKey(nameof(Sku))]
public partial class Product : EntityBase
{
    [Required]
    [MaxLength(20)]
    [Searchable(1, Dictionary = "simple")]
    [Property<DataTypes.IdText>("SKU",
        Description = "Your part number for this product")]
    public partial string Sku { get; set; }

    [Searchable(2)]
    [Property<DataTypes.MultilineText>("Description",
        Description = "The description of the product that is used in sales documents.")]
    public partial string SalesDescription { get; set; }

    [Property<DataTypes.Decimal>("Unit weight")]
    public partial decimal UnitWeight { get; set; }

    [Property<ProductUnitPrice>("Cost",
        Description = "The cost of the product. This is often used to calculate the price")]
    public partial decimal Cost { get; set; }

    [Property<DataTypes.Website>("Website")]
    public partial string Website { get; set; }

    [Property<DataTypes.Text>("Color")]
    [DefaultValue("#ffffff")]
    public partial string Color { get; set; }

    // Virtual — computed, not in database
    [Virtual]
    [Property<DataTypes.Boolean>("Require all units to be sold in integer quantities")]
    public partial bool RequireIntegerQuantitiesForAllUOMs { get; set; }
}