oracle / dotnet-db-samples

.NET code samples for Oracle database developers #OracleDotNet
http://otn.oracle.com/dotnet
MIT License
408 stars 190 forks source link

CreateTable adds redundant rowversion trigger when column marked as concurrency token #390

Open harryKont opened 3 days ago

harryKont commented 3 days ago

When a column is marked as a concurrency token upon entity creation EF generates a migration that looks like this

migrationBuilder.CreateTable(
                name: "Table",
                schema: "Default",
                columns: table => new
                {
                    ID = table.Column<long>(type: "NUMBER(19)", nullable: false),
                    VERSION = table.Column<long>(
                        type: "NUMBER(19)",
                        rowVersion: true,
                        nullable: false
                    ),
                },

Generating the script using dotnet ef migrations script adds a trigger like this for the Version column

DECLARE
    v_Count INTEGER;
BEGIN
SELECT COUNT(*) INTO v_Count FROM "Default"."EF_MIGRATION_HISTORY" WHERE "MIGRATION_ID" = N'20240205231307_InitialCreate';
IF v_Count = 0 THEN

    CREATE OR REPLACE TRIGGER "Default"."rowversion_TABLE"
    BEFORE INSERT OR UPDATE ON "Default"."TABLE"
    FOR EACH ROW
    BEGIN
      :NEW."VERSION" := UTL_RAW.CAST_FROM_BINARY_DOUBLE(UTL_RAW.CAST_TO_BINARY_DOUBLE(NVL(:OLD."VERSION", '0000000000000000')) + 1);
    END;
END IF;
END;

/

However, when adding the same column after creation using AddColumn it is not adding the trigger

migrationBuilder.AddColumn<long>(
                name: "VERSION",
                schema: "OS",
                table: "ORDERS",
                type: "NUMBER(19)",
                rowVersion: true,
                nullable: false,
                defaultValue: 0L);

We would like to have more control over the row version trigger so we ended up using the second method.

Although this workaround worked for us I find it weird that the behaviour differs based on the way the column was added by the migration. This started happening at some point between EF Core 6 and 8 and it is a breaking change as with previous EF versions we didn't get the trigger automatically created by EF. We had to create it manually. In later versions EF started generating the trigger but this is problematic because every time we drop and recreate the DB it tries to add a trigger that was not there in the initial version. Ideally we would like to have more control of the rowversion trigger and be able to use our existing trigger (using a sequence) instead of the automatically created one that seems to just bump the existing row version column value on INSERT OR UPDATE

alexkeh commented 3 days ago

Which two ODP.NET EF Core versions exhibited the behavior difference? Presumably, you upgraded from one version to another to see the issue.

You can turn on EF Core tracing or ODP.NET tracing to see the difference in what the providers are generating for their scripts and perhaps some basis for that difference.