Skip to content

Settings.ViewProcessing

Simon Hughes edited this page Aug 30, 2026 · 1 revision

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

What it does

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.

When to use it

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.

Gotchas

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.

See also

Clone this wiki locally