-
Notifications
You must be signed in to change notification settings - Fork 226
Settings.UsePrivateSetterForComputedColumns
Gives computed columns a private set; so your code cannot try to assign a value the database works out for itself.
| Type | bool |
| Default | true |
| Applies to | EF 6 and EF Core |
| Databases | All. Only has an effect where a database reports computed columns: SQL Server, PostgreSQL, MySQL and Oracle |
In Database.tt? |
Yes |
A computed column is one the database calculates from other columns. You never write to it - SQL Server rejects the attempt - and its value changes on its own whenever the columns it is built from change.
That is awkward for a POCO, because a plain { get; set; } property invites exactly the assignment that will
fail. Worse, it fails at SaveChanges() rather than at compile time, so you find out at run time.
When this setting is true, the generator gives those properties a private setter. EF Core can still populate
them when it materialises an entity, because it writes through the private setter directly, but your own code
cannot. The mistake becomes a compiler error instead of a run-time one.
Every example on this page comes from this table. DisplayLabel is the computed column:
CREATE TABLE dbo.Product
(
ProductId int NOT NULL IDENTITY(1, 1),
ProductName nvarchar(100) NOT NULL,
UnitPrice decimal(18, 2) NOT NULL CONSTRAINT DF_Product_UnitPrice DEFAULT ((0)),
Notes nvarchar(max) NULL,
CategoryId int NOT NULL,
DisplayLabel AS (CONVERT(nvarchar(150), ProductName + ' (' + CONVERT(varchar(20), UnitPrice) + ')')),
CONSTRAINT PK_Product PRIMARY KEY (ProductId),
CONSTRAINT FK_Product_Category FOREIGN KEY (CategoryId) REFERENCES dbo.Category (CategoryId)
); // Product
public class Product
{
public int ProductId { get; set; } // ProductId (Primary key)
public string ProductName { get; set; } // ProductName (length: 100)
public decimal UnitPrice { get; set; } // UnitPrice
public string Notes { get; set; } // Notes
public int CategoryId { get; set; } // CategoryId
public string DisplayLabel { get; private set; } // DisplayLabel (length: 150)
// Foreign keys
/// <summary>
/// Parent Category pointed by [Product].([CategoryId]) (FK_Product_Category)
/// </summary>
public Category Category { get; set; } // FK_Product_Category
public Product()
{
UnitPrice = 0m;
}
} // Product
public class Product
{
public int ProductId { get; set; } // ProductId (Primary key)
public string ProductName { get; set; } // ProductName (length: 100)
public decimal UnitPrice { get; set; } // UnitPrice
public string Notes { get; set; } // Notes
public int CategoryId { get; set; } // CategoryId
public string DisplayLabel { get; set; } // DisplayLabel (length: 150)
// Foreign keys
/// <summary>
/// Parent Category pointed by [Product].([CategoryId]) (FK_Product_Category)
/// </summary>
public Category Category { get; set; } // FK_Product_Category
public Product()
{
UnitPrice = 0m;
}
}One line differs: DisplayLabel goes from { get; private set; } to { get; set; }. Everything else,
including the entity configuration, is identical - the setting changes the property declaration and nothing
else.
Either way the entity configuration marks the column as database-generated, so EF never tries to write it:
builder.Property(x => x.DisplayLabel).HasColumnName(@"DisplayLabel").HasColumnType("nvarchar(150)").IsRequired(false).HasMaxLength(150).ValueGeneratedOnAddOrUpdate();Leave it at true. It is the safer default and it costs nothing.
Set it to false when something outside your control needs to write to the property:
-
Object mappers that use property setters. AutoMapper, Dapper's
QueryFirst, and most JSON deserialisers need a public setter. If you map a DTO onto an entity and the mapper hits the computed property, it will either throw or silently skip the value depending on the library. -
System.Text.Jsondeserialisation ignores properties without a public setter, so a round-trip through JSON quietly loses the value. - Test fixtures that build an entity by hand and want to set the computed value to something predictable.
This is about the C# property, not the database. Setting it to false does not make the column writable.
The database still rejects a write to a computed column, and EF still will not attempt one, because the
configuration marks it ValueGeneratedOnAddOrUpdate() regardless.
Settings.UsePropertyInitialisers does not interact with it. Computed columns never get an initialiser,
because there is no default to initialise them to.
A private setter needs a partial class if you want to widen it later. You cannot add a public setter to a generated property from another file. Either turn this setting off or expose a separate writable property in your own partial class.
- Settings.UsePropertyInitialisers - the other setting that changes how a property is declared
- Settings.IncludeColumnsWithDefaults - column defaults, which are a different thing from computed columns
- Settings Reference
- Settings A-Z - every setting, with a page each
- Common Settings Types Explained
- Settings Callbacks
- Settings runtime values and helpers
- Filtering
- Full Control Over the Generated Code
- Enum Generation from Table Data
- Owned Entities
- JSON column support
- Global Query Filters
- Extended Property Names Feature
- Partial Properties
- File-Scoped Namespaces
- Data Annotations
- Spatial Types
- HierarchyId
- RowVersion and TimeStamp columns
- Lazy Loading
- Stored proc result sets
- Custom File-Based Templates
- Extra entities via partial classes
- INotifyPropertyChanged
- Syntax colour for T4