-
Notifications
You must be signed in to change notification settings - Fork 226
Settings.ForeignKeyFilterFunc
Runs for every foreign key, so you can drop a relationship entirely or keep it while suppressing the collection property it would add to the parent.
| Type |
Func<ForeignKey, ForeignKey> - return the key to keep it, null to drop it |
| Default | Returns the key unchanged |
| Applies to | EF 6 and EF Core |
| Databases | All |
In Database.tt? |
Yes, with commented-out examples |
Every foreign key normally produces two navigation properties: the forward one on the child
(Product.Category) and the reverse one on the parent (Category.Products). The reverse one is the
expensive half - it is a collection, it is instantiated in the constructor, and on a lookup table referenced
by thirty other tables it means thirty collections nobody uses.
This callback gives you two levers:
-
Return
nulland the relationship disappears completely. No navigation property at either end, noHasForeignKeyin the configuration. -
Set
fk.IncludeReverseNavigation = falseand return the key. The forward property survives, the collection on the parent does not.
The second is the one you will reach for.
Suppressing every reverse navigation:
Settings.ForeignKeyFilterFunc = delegate(ForeignKey fk)
{
fk.IncludeReverseNavigation = false;
return fk;
}; // Category
public class Category
{
public int CategoryId { get; set; } // CategoryId (Primary key)
public string CategoryName { get; set; } // CategoryName (length: 50)
// Reverse navigation
/// <summary>
/// Child Products where [Product].[CategoryId] point to this entity (FK_Product_Category)
/// </summary>
public ICollection<Product> Products { get; set; } // Product.FK_Product_Category
public Category()
{
Products = new List<Product>();
}
} // Category
public class Category
{
public int CategoryId { get; set; } // CategoryId (Primary key)
public string CategoryName { get; set; } // CategoryName (length: 50)
}Category loses its Products collection and, with it, the constructor that initialised it. Product keeps
its Category property and the mapping is unchanged, so the relationship is still fully configured - you have
just stopped navigating it from the parent end.
Lookup tables. Status, Country, Currency - referenced everywhere, navigated from almost nowhere.
Suppressing the reverse navigation on those alone often removes most of the noise:
Settings.ForeignKeyFilterFunc = delegate(ForeignKey fk)
{
var lookups = new[] { "Status", "Country", "Currency" };
if (lookups.Contains(fk.PkTableName))
fk.IncludeReverseNavigation = false;
return fk;
};Audit columns pointing at a user table. CreatedByUserId and ModifiedByUserId both point at User,
which gets two collections it will never use, with awkward generated names.
Serialisation. A cycle between parent and child is what makes naive JSON serialisation of an entity graph
blow up. Cutting the reverse navigation removes the cycle at the source, which is blunter but simpler than
[JsonIgnore] everywhere.
Relationships EF should not know about. Returning null for a foreign key into a table outside your
bounded context stops it appearing in the model at all.
Returning null removes the mapping, not just the property. Without HasForeignKey, EF Core no longer
knows the columns are related. Cascade behaviour, required/optional inference and Include all go with it. If
you only want to hide a property, use IncludeReverseNavigation instead.
fk.PkTableName and fk.FkTableName are the database names, before renaming and before any schema
prefix. Match on those, not on the generated class name.
Most of ForeignKey is read-only. FkColumn, PkColumn, ConstraintName and the rest are readonly
fields set in the constructor. The three you can assign are IncludeReverseNavigation, AccessModifier and
UniqueName.
AccessModifier changes the property's visibility, which is occasionally handy - "internal" keeps a
navigation property out of a public API surface while leaving it usable inside the assembly.
It cannot add relationships. For a relationship the database does not declare - between a view and a
table, say - use Settings.AddExtraForeignKeys.
- Settings.ForeignKeyName - naming the properties this keeps
-
Settings.ForeignKeyAnnotationsProcessing - the
[JsonIgnore]route, if you want the property but not the serialisation - Settings.AddExtraForeignKeys - adding relationships
- 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