Skip to content

Settings.ForeignKeyFilterFunc

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

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

What it does

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 null and the relationship disappears completely. No navigation property at either end, no HasForeignKey in the configuration.
  • Set fk.IncludeReverseNavigation = false and return the key. The forward property survives, the collection on the parent does not.

The second is the one you will reach for.

Example

Suppressing every reverse navigation:

Settings.ForeignKeyFilterFunc = delegate(ForeignKey fk)
{
    fk.IncludeReverseNavigation = false;
    return fk;
};

Before

    // 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>();
        }
    }

After

    // 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.

When to use it

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.

Gotchas

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.

See also

Clone this wiki locally