Skip to content

How to implement OnPropertyChanged

Simon Hughes edited this page Sep 10, 2026 · 8 revisions

This guide shows how to add INotifyPropertyChanged support to generated POCO classes. Three of the four steps are ordinary Database.tt settings; the last edits one line of the property template.

Approach

Steps 1 to 3 live in Database.tt and survive every upgrade. Step 4 changes how every property is emitted, which means editing the property template inside EF.Reverse.POCO.v4.ttinclude; that file is replaced on upgrade, so keep the edit as a patch and reapply it.

Alternative: Partial Classes (Simpler)

If you only need OnPropertyChanged for specific scenarios, use the partial modifier and implement the logic in a separate file without touching the generator.

Full Implementation

Step 1: Set the table base class in Database.tt

Settings.UpdateTable = delegate(Table table)
{
    table.BaseClasses = " : INotifyPropertyChanged";
};

Step 2: Add required namespaces in Database.tt

Settings.AdditionalNamespaces = new List<string>
{
    "System.ComponentModel",
    "System.Runtime.CompilerServices"
};

Step 3: Write the event/method boilerplate inside each class

In Database.tt, use WriteInsideClassBody:

Settings.WriteInsideClassBody = delegate(Table t)
{
    return
        "    public event PropertyChangedEventHandler PropertyChanged;" + Environment.NewLine +
        "    protected virtual void OnPropertyChanged([CallerMemberName] string name = null)" + Environment.NewLine +
        "    {" + Environment.NewLine +
        "        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));" + Environment.NewLine +
        "    }" + Environment.NewLine;
};

Step 4: Modify the property template

This step requires editing the Mustache property template inside EF.Reverse.POCO.v4.ttinclude.

Warning: EF.Reverse.POCO.v4.ttinclude is replaced when you upgrade the generator, so keep this edit as a patch and reapply it afterwards.

In the property template, find the line that generates properties:

public {{#if OverrideModifier}}override {{/if}}{{WrapIfNullable}} {{NameHumanCase}}

Replace it with a backing field + property with change notification:

private {{WrapIfNullable}} _{{NameHumanCase}};{{#newline}}
public {{#if OverrideModifier}}override {{/if}}{{WrapIfNullable}} {{NameHumanCase}} { get { return _{{NameHumanCase}}; } {{PrivateSetterForComputedColumns}}set { if(value != _{{NameHumanCase}}) { _{{NameHumanCase}} = value; OnPropertyChanged(); } } }{{PropertyInitialisers}}{{InlineComments}}{{#newline}}

Result

Generated classes will look like:

public partial class Order : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string name = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
    }

    private int _orderId;
    public int OrderId
    {
        get { return _orderId; }
        set { if (value != _orderId) { _orderId = value; OnPropertyChanged(); } }
    }

    private string _customerName;
    public string CustomerName
    {
        get { return _customerName; }
        set { if (value != _customerName) { _customerName = value; OnPropertyChanged(); } }
    }
}

Clone this wiki locally