-
Notifications
You must be signed in to change notification settings - Fork 226
Extended Property Names Feature
This feature enhancement allows you to access database extended properties by their name in addition to their value. Previously, only the extended property value was available as a string in column.ExtendedProperty. Now you can access individual extended properties by name using the new column.ExtendedProperties dictionary.
First, add extended properties to your database column:
-- Add a JsonPropertyName extended property to a column
EXEC sp_addextendedproperty
@name = N'JsonPropertyName',
@value = N'id',
@level0type = N'SCHEMA',
@level0name = N'dbo',
@level1type = N'TABLE',
@level1name = N'YourTable',
@level2type = N'COLUMN',
@level2name = N'SystemId'
-- You can add multiple extended properties to the same column
EXEC sp_addextendedproperty
@name = N'CustomValidator',
@value = N'EmailValidator',
@level0type = N'SCHEMA',
@level0name = N'dbo',
@level1type = N'TABLE',
@level1name = N'YourTable',
@level2type = N'COLUMN',
@level2name = N'EmailAddress'In your Database.tt, use the UpdateColumn delegate. Settings.ApplyJsonPropertyNameAttribute is the
built-in helper that turns a JsonPropertyName extended property into an attribute; the other Apply* helpers
are unrelated to extended properties but are shown because the shipped template calls all four together:
Settings.UpdateColumn = delegate (Column column, Table table, List<EnumDefinition> enumDefinitions, List<JsonColumnMapping> jsonColumnMappings)
{
// Your own rules go here, reading column.ExtendedProperties
Settings.ApplyJsonPropertyNameAttribute(column);
Settings.ApplyJsonColumnMappings(column, table, jsonColumnMappings);
Settings.ApplyDataAnnotations(column);
Settings.ApplyEnumTypeReplacement(column, table, enumDefinitions);
};Given the SQL setup above, the generated code will look like:
public class YourTable
{
[JsonPropertyName("id")]
public string SystemId { get; set; }
[CustomValidator(typeof(EmailValidator))]
public string EmailAddress { get; set; }
}| Database | Extended Property Name Support | Notes |
|---|---|---|
| SQL Server | ✅ Yes | Reads real extended property names from sys.extended_properties. Azure SQL Database does not report extended properties, so this read is skipped there |
| PostgreSQL |
pg_description comments, read under the fixed name Comment
|
|
| MySQL |
COLUMN_COMMENT and TABLE_COMMENT, read under the fixed name Comment
|
|
| Oracle |
ALL_COL_COMMENTS and ALL_TAB_COMMENTS, read under the fixed name Comment
|
|
| SQLite | ❌ No | SQLite stores no column comments at all |
Only SQL Server has genuinely named properties. On the other three, everything arrives in
column.ExtendedProperties["Comment"], because those databases have one comment per object and no name to
give it. column.ExtendedProperty (the plain string) is populated everywhere the dictionary is.
- JSON Property Mapping: Map database column names to different JSON property names
- Custom Validation: Specify validators via extended properties
- API Visibility: Control which properties appear in APIs
- Documentation: Store property-specific documentation by category
- Feature Flags: Enable/disable features per column
- Custom Attributes: Add any custom attributes based on extended properties
-- Add multiple extended properties to one column
EXEC sp_addextendedproperty @name = N'JsonPropertyName', @value = N'userId',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'Orders',
@level2type = N'COLUMN', @level2name = N'UserId'
EXEC sp_addextendedproperty @name = N'Validator', @value = N'Required|Guid',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'Orders',
@level2type = N'COLUMN', @level2name = N'UserId'
EXEC sp_addextendedproperty @name = N'Description', @value = N'The unique identifier for the user',
@level0type = N'SCHEMA', @level0name = N'dbo',
@level1type = N'TABLE', @level1name = N'Orders',
@level2type = N'COLUMN', @level2name = N'UserId'Settings.UpdateColumn = delegate (Column column, Table table, List<EnumDefinition> enumDefinitions, List<JsonColumnMapping> jsonColumnMappings)
{
// Access all three extended properties
if (column.ExtendedProperties.ContainsKey("JsonPropertyName"))
column.Attributes.Add($"[JsonPropertyName(\"{column.ExtendedProperties["JsonPropertyName"]}\")]");
if (column.ExtendedProperties.ContainsKey("Validator"))
{
var validators = column.ExtendedProperties["Validator"].Split('|');
foreach (var validator in validators)
column.Attributes.Add($"[{validator}]");
}
if (column.ExtendedProperties.ContainsKey("Description"))
column.SummaryComments = column.ExtendedProperties["Description"];
};- Extended property names are case-insensitive (the dictionary uses
StringComparer.OrdinalIgnoreCase) - Where a database has no named extended properties, the fixed name
Commentis used - see the table above - Property names are trimmed of whitespace
-
column.ExtendedProperty(the plain string) still holds the value and is unchanged, so anything you wrote against it keeps working - The reading itself happens in the
efrpgdotnet tool, not in the T4 template. The values reach your callbacks the same way regardless
- 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