Skip to content

Settings.AddExtraForeignKeys

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

Settings.AddExtraForeignKeys

Declares relationships the database does not - between a view and a table, across schemas, or where the constraint was never created.

Type Action<IDbContextFilter, Generator, List<ForeignKey>, Tables>
Default Adds nothing
Applies to EF 6 and EF Core
Databases All
In Database.tt? Yes, with commented-out examples

What it does

The generator reads foreign keys from the database. Anything not declared there does not exist as far as it is concerned - and there are three common reasons a real relationship is not declared:

  • Views have no foreign keys. A view joining onto a table is a relationship you can see and the database cannot.
  • The constraint was never created. Plenty of production schemas rely on convention rather than constraints, for performance or for history.
  • It crosses a boundary the database will not span - a linked server, or two databases.

This callback lets you add them by hand. gen.AddRelationship does the work:

Settings.AddExtraForeignKeys = delegate(IDbContextFilter filter, Generator gen, List<ForeignKey> foreignKeys, Tables tablesAndViews)
{
    // Orders -> Invoices view, on OrderID
    gen.AddRelationship(filter, foreignKeys, tablesAndViews,
        "orders_to_invoices",
        Settings.DefaultSchema, "Orders",   "OrderID",   // principal: schema, table, column
        "dbo",                  "Invoices", "OrderID",   // dependent:  schema, table, column
        null, null, true);                               // parentName, childName, isUnique
};

There is an overload taking string[] for both column arguments, for a composite key:

gen.AddRelationship(filter, foreignKeys, tablesAndViews,
    "orderDetails_to_invoices",
    Settings.DefaultSchema, "Order Details", new[] { "OrderID", "ProductID" },
    "dbo",                  "Invoices",      new[] { "OrderID", "ProductID" },
    null, null, true);

The two name arguments

parentName and childName set the navigation property names directly, and they win over Settings.ForeignKeyName - that callback is never consulted for a relationship added here. Pass null for both to get the usual naming.

gen.AddRelationship(filter, foreignKeys, tablesAndViews,
    "orders_to_ordersQry",
    Settings.DefaultSchema, "Orders",     "OrderID",
    "dbo",                  "Orders Qry", "OrderID",
    "ParentFkName", "ChildFkName", true);

When to use it

Views you want to navigate to. This and Settings.ViewProcessing go together: one gives the view a key, the other connects it to the rest of the model.

A legacy schema with no declared constraints, where you know the relationships and want EF to know them too.

Do not use it to paper over a missing constraint you could add. If the relationship is real and the database could enforce it, adding the constraint fixes this for everyone rather than for your .tt only.

Gotchas

Both ends must exist and be generated. Filter one of them out and the relationship refers to a table that is not there. The callback runs after filtering, so the table you name may already be gone.

Schema and table names are database names, before renaming - "Order Details", space included.

EF will not enforce it, and neither will the database. You have told EF a relationship exists. If the data does not honour it, you get orphaned references at run time and no error at generation time.

isUnique chooses one-to-one over one-to-many. Getting it wrong gives you a collection where you wanted a reference, or the reverse.

The Generator parameter is the live generator, and AddRelationship is the supported way to use it. Reaching further into it is not, and the internals change between versions.

See also

Clone this wiki locally