-
Notifications
You must be signed in to change notification settings - Fork 226
Settings.ViewProcessing
Tells the generator which columns of a view identify a row, because a view has no primary key to read.
| Type | Action<Table> |
| Default | Does nothing, which means every non-nullable column becomes part of the key |
| Applies to | EF 6 and EF Core |
| Databases | All that have views: SQL Server, PostgreSQL, MySQL, Oracle, SQLite |
In Database.tt? |
Yes, with a commented-out example |
EF needs a key for every entity so it can track and identify instances. Tables declare one; views do not.
Faced with a view and no key, the generator falls back to a guess: every non-nullable column becomes part of the primary key. On a five-column view with three non-nullable columns you get a three-part composite key, which is almost never right and is occasionally catastrophic - two rows sharing those values collapse into one as far as change tracking is concerned.
This callback is where you say what the key actually is.
Settings.ViewProcessing = delegate(Table view)
{
switch (view.DbName)
{
case "ActiveStudent":
view.Columns.Single(c => c.DbName == "StudentId").IsPrimaryKey = true;
break;
case "OrderSummary":
// A composite key needs each part marked
foreach (var c in view.Columns.Where(c => c.DbName == "OrderId" || c.DbName == "ProductId"))
c.IsPrimaryKey = true;
break;
}
};Mark any column and the fallback is abandoned - only the columns you marked form the key.
Every time you generate a view you intend to query by key or track. The default guess is only acceptable for a view you always read in bulk and never update.
Views you map as read-only still benefit. A wrong key means EF de-duplicates rows that are not duplicates, so a query can silently return fewer rows than the view does.
If the view genuinely has no key - an aggregate, a report - consider whether it should be an entity at all.
ToSqlQuery or a keyless entity type is a better fit, and neither is something this generator produces
directly.
It only runs for views. Tables never reach it, whatever you write.
Views must be turned on first. FilterSettings.IncludeViews = true - and the shipped Database.tt has
that line commented out, so by default no views are read and this callback never fires.
Single throws if the column name is wrong. A typo, or a column that was renamed, and generation fails
with an exception rather than a helpful message. SingleOrDefault with a null check is kinder to future you.
Match on DbName, not NameHumanCase. By the time this runs, PascalCasing and any
Settings.TableRename have been applied to the view's name, but DbName is stable.
Marking no columns is not the same as marking none deliberately. If you want a genuinely keyless view, this callback cannot express it - the fallback will still apply.
Foreign keys between views and tables do not exist in the database, so the generator cannot read them. Add
them with Settings.AddExtraForeignKeys, which is the natural companion to
this setting.
- Settings.AddExtraForeignKeys - relationships involving views
-
Filtering -
FilterSettings.IncludeViews - Settings.UpdateTable - which also runs for views
- 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