-
Notifications
You must be signed in to change notification settings - Fork 226
Settings.UpdateTable
Runs your code once for every table and view after naming, so you can attach a base class, add class-level attributes, or drop the table entirely.
| Type | Action<Table> |
| Default | Does nothing |
| Applies to | EF 6 and EF Core |
| Databases | All |
In Database.tt? |
Yes, with commented-out examples |
Where Settings.TableRename runs before naming, this runs after it, so the table
arrives fully formed: NameHumanCase is the final class name, Columns is populated, and the schema is
known. It is called just before Settings.UpdateColumn runs for that table's columns.
You mutate the Table in place. The fields worth knowing:
| Field | Type | Effect |
|---|---|---|
NameHumanCase |
string |
The C# class name. Assign to rename |
DbName |
string |
The database table name, for matching on |
Schema.DbName |
string |
The schema, for matching on |
BaseClasses |
string |
Written straight after the class name, colon included: " : IAuditable"
|
Attributes |
List<string> |
Class-level attributes, brackets included |
AdditionalComment |
string |
An extra comment above the class |
Columns |
List<Column> |
Every column, so you can decide based on what the table contains |
HasPrimaryKey |
bool |
Useful for spotting views and junction tables |
RemoveTable |
bool |
true drops the table from generation entirely |
IsView |
bool |
Distinguishes a view from a table |
PluralNameOverride |
string |
Overrides just the DbSet name, leaving the class alone |
DbSetModifier |
string |
The access modifier on the DbSet property, "public" by default |
Giving Product a base interface and an attribute:
Settings.UpdateTable = delegate(Table table)
{
if (table.NameHumanCase == "Product")
{
table.BaseClasses = " : IAuditable";
table.Attributes.Add("[Serializable]");
}
};public class Product // Product
[Serializable]
public class Product : IAuditable
{
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;
}
}Note the leading space and colon in " : IAuditable". The value is written verbatim after the class name, so
the punctuation is yours to supply.
A base class chosen by what the table contains. The most useful pattern by far - the rule lives in one place and picks up new tables automatically:
Settings.UpdateTable = delegate(Table table)
{
var audit = new[] { "createdby", "createdon", "modifiedby", "modifiedon" };
if (audit.All(a => table.Columns.Any(c => c.NameHumanCase.ToLower() == a)))
table.BaseClasses = " : AuditableEntity";
};Pair it with column.ExistsInBaseClass = true in Settings.UpdateColumn so those
four columns are declared once on the base class rather than on every entity.
Dropping a table you cannot filter out. RemoveTable = true is the late escape hatch for a table that has
to be read - because something else has a foreign key to it - but must not be generated.
Marking entities for a serialiser or a validator, with [Serializable], [DataContract] or your own.
BaseClasses is a raw string, not a list. Setting it twice overwrites; to add a second interface, append
to it. And the generator does not check the type exists - a typo becomes a compiler error in the generated
file.
RemoveTable does not remove the relationships pointing at it. Other entities may still have navigation
properties expecting the class you just dropped. Prefer a filter where you can, and use
RemoveTable only when the table has to be read for something else's sake.
It runs for views too. Check table.IsView before applying a rule that only makes sense for tables. For
views specifically, Settings.ViewProcessing runs later and is
the place to declare which columns form the key.
Renaming here is late. NameHumanCase is assignable, but the reverse navigation properties on other
tables have not necessarily been named yet, and the schema prefix has already been applied. For a rename that
propagates cleanly, use Settings.TableRename.
- Settings.TableRename - the earlier hook, for names
-
Settings.UpdateColumn - the same idea one level down, and where
ExistsInBaseClasslives - Settings.WriteInsideClassBody - adding members rather than attributes
- Settings Callbacks | 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