PropertyDeleted Event
Summary
The PropertyDeleted event fires during database upgrade when a column that exists in the current database is no longer present in the EF Core model. Use it to migrate data from the column being removed to another column or table before the column is dropped.
When does it fire?
PropertyDeleted fires when the schema comparison detects a column in the live database that does not exist in the new EF Core model. The subscriber runs before the column is dropped, so data can still be read from it.
Schema comparison → Column removed detected → PropertyDeleted subscribers → Column dropped
Syntax
[PropertyDeleted(typeof(EntityType), "PropertyName")]
public void MethodName(PropertyRemovedEventArgs args)
{
args.UpgradeScript("SQL script here");
}
Attribute
| Parameter | Type | Description |
|---|---|---|
entityType |
Type |
The entity type (used as typeof(Entity)) |
propertyName |
string |
The property (column) name being removed |
Event Args (PropertyRemovedEventArgs)
| Property/Method | Description |
|---|---|
TableName |
The database table name |
ColumnName |
The database column name being removed |
UpgradeScript(sql) |
Adds a SQL script to execute during the upgrade |
ReadFromDatabase(sql) |
Reads data from the database to help generate migration scripts |
Scenarios
1. Migrating data to a replacement column
When an old Price column is replaced by UnitPrice, copy the data before Price is dropped.
public class DataBaseUpgrade
{
[PropertyDeleted(typeof(Product), "Price")]
public void MigratePriceToUnitPrice(PropertyRemovedEventArgs args)
{
var sql = $@"
UPDATE ""{args.TableName}""
SET ""UnitPrice"" = ""{args.ColumnName}""
WHERE ""UnitPrice"" IS NULL OR ""UnitPrice"" = 0;";
args.UpgradeScript(sql);
}
}
2. Archiving data before column removal
When a column is being removed, save the data to an audit table before it is dropped.
public class DataBaseUpgrade
{
[PropertyDeleted(typeof(Customer), "LegacyCode")]
public void ArchiveLegacyCode(PropertyRemovedEventArgs args)
{
var sql = $@"
INSERT INTO ""AuditLog"" (""TableName"", ""ColumnName"", ""EntityGuid"", ""OldValue"", ""ArchivedAt"")
SELECT '{args.TableName}', '{args.ColumnName}', ""Guid"", ""{args.ColumnName}""::text, NOW()
FROM ""{args.TableName}""
WHERE ""{args.ColumnName}"" IS NOT NULL;";
args.UpgradeScript(sql);
}
}
3. Reading data to inform migration logic
Use ReadFromDatabase to inspect existing data before generating the migration script.
public class DataBaseUpgrade
{
[PropertyDeleted(typeof(SalesOrder), "OldStatus")]
public void MigrateOldStatusValues(PropertyRemovedEventArgs args)
{
var data = args.ReadFromDatabase(
$"SELECT DISTINCT \"{args.ColumnName}\" FROM \"{args.TableName}\" WHERE \"{args.ColumnName}\" IS NOT NULL");
foreach (System.Data.DataRow row in data.Rows)
{
var oldValue = row[args.ColumnName]?.ToString();
var newValue = oldValue == "InProgress" ? "Active" : "Closed";
args.UpgradeScript(
$"UPDATE \"{args.TableName}\" SET \"Status\" = '{newValue}' WHERE \"{args.ColumnName}\" = '{oldValue}';");
}
}
}
Notes
- The
[PropertyDeleted]attribute takes aTypeparameter (not a string name) for the entity, unlike[PropertyAdded]which takes a string name. - The subscriber runs before the column is dropped, so you can still read from the column being removed.
- Use
ReadFromDatabasewhen you need to inspect the existing data to generate appropriate migration scripts. - If no subscriber handles the property deletion, the column is simply dropped and its data is lost.