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 INSERT data into it.
  • Use ReadFromDatabase to 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.