EntityAdded Event
Summary
The EntityAdded event fires during database upgrade when a new table is detected in the EF Core model that does not exist in the current database. Use it to seed initial data into the new table or migrate data from other tables.
When does it fire?
EntityAdded fires when the schema comparison detects a table in the new EF Core model that does not exist in the live database. The subscriber runs after the table has been created, so you can insert data into it.
Schema comparison → New table detected → Table created → EntityAdded subscribers
Tenant feature behavior
EntityAdded follows the tenant's EF Core model. If the table is new for that tenant,
the subscriber runs. The subscriber class's namespace does not control whether it
runs.
If feature trimming removes the table from the tenant model, there is no added-table event. If the table is added to the model later, the subscriber runs during that later schema upgrade.
Syntax
[EntityAdded(typeof(EntityType))]
public void MethodName(EntityAddedEventArgs args)
{
args.UpgradeScript("SQL script here");
}
Attribute
| Name | Type | Meaning |
|---|---|---|
entityType |
Type |
The entity type being added (used as typeof(Entity)) |
FeatureName |
string? |
The feature that the SQL needs |
Set FeatureName when the SQL uses a table or column from another feature.
Core skips this subscriber when that feature is off for the tenant.
[EntityAdded(
typeof(ProductAccount),
FeatureName = "Benevia.ERP.Model.Financials")]
Leave FeatureName empty when the SQL does not need another feature. The
subscriber class's namespace does not set this value.
Event Args (EntityAddedEventArgs)
| Property/Method | Description |
|---|---|
TableName |
The database table name that was created |
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. Seeding initial data into a new table
When a new Category entity is added, seed it with default categories.
public class DataBaseUpgrade
{
[EntityAdded(typeof(Category))]
public void SeedDefaultCategories(EntityAddedEventArgs args)
{
args.UpgradeScript($@"
INSERT INTO ""{args.TableName}"" (""Guid"", ""Name"", ""SortOrder"")
VALUES
(gen_random_uuid(), 'General', 1),
(gen_random_uuid(), 'Premium', 2);");
}
}
2. Migrating data from an existing table into a new one
When a new ProductVariant table replaces inline variant columns on Product, migrate existing data.
public class DataBaseUpgrade
{
[EntityAdded(typeof(ProductVariant))]
public void MigrateVariantData(EntityAddedEventArgs args)
{
var products = args.ReadFromDatabase(
"""SELECT "Guid", "VariantName", "VariantSku" FROM "Product" WHERE "VariantName" IS NOT NULL""");
foreach (System.Data.DataRow row in products.Rows)
{
var productGuid = row["Guid"];
var name = row["VariantName"];
var sku = row["VariantSku"];
args.UpgradeScript($@"
INSERT INTO ""{args.TableName}"" (""Guid"", ""ProductGuid"", ""Name"", ""Sku"")
VALUES (gen_random_uuid(), '{productGuid}', '{name}', '{sku}');");
}
}
}
3. Copying configuration data from a legacy table
When a new settings table is introduced, copy configuration from the old structure.
public class DataBaseUpgrade
{
[EntityAdded(typeof(CompanySetting))]
public void MigrateFromLegacySettings(EntityAddedEventArgs args)
{
args.UpgradeScript($@"
INSERT INTO ""{args.TableName}"" (""Guid"", ""Key"", ""Value"")
SELECT gen_random_uuid(), ""SettingKey"", ""SettingValue""
FROM ""LegacyConfig""
WHERE ""SettingKey"" IS NOT NULL;");
}
}
Notes
- The
[EntityAdded]attribute can be applied multiple times to the same method if the same logic applies to multiple new entities. - The table has already been created by the time the subscriber runs, so you can
INSERTdata into it. - Use
ReadFromDatabaseto read data from existing tables that need to be migrated into the new table. - EntityAdded is typically used together with EntityDeleted when replacing one table with another.