-
Notifications
You must be signed in to change notification settings - Fork 226
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 |
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);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);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.
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.
- Settings.ViewProcessing - giving the view a key, which it needs first
- Settings.ForeignKeyName - naming relationships the database did declare
- Settings.ForeignKeyFilterFunc - removing them
- 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