UpdateNonDefaults vs nullable enum values

Hi,

I’m not sure if this is the intended behavior or not.

If an enum is set to nullable on a DTO and I use Db.UpdateNonDefaultsAsync, providing the first enum value does not update the database. It seems the first enum value is considered the default, even when the enum is nullable.

Here’s a minimal reproducible example:

// Migration1000
private class Tenant
{
    [AutoIncrement]
    public long? Id { get; set; }

    [Required]
    public TenantStatus? TenantStatus { get; set; }
}

// In ServiceModel
public class Tenant
{
    [AutoIncrement]
    public long? Id { get; set; }

    [Required]
    [ValidateNotNull]
    public TenantStatus? TenantStatus { get; set; }
}

public enum TenantStatus
{
    Active,
    Archived,
    Disabled,
    Unknown,
}

[Route("/tenant/{Id}", "PATCH")]
[ValidateHasRole("Admin")]
public class UpdateTenant : IPatchDb<Tenant>, IReturn<IdResponse>
{
    [ValidateGreaterThan(0)]
    public long Id { get; set; }

    public TenantStatus? TenantStatus { get; set; }
}

// In ServiceInterface
public async Task<object> PatchAsync(UpdateTenant req)
{
    // here req.TenantStatus == TenantStatus.Active
    var tenant = await Db.SingleByIdAsync<Tenant>(req.Id);
    tenant.TenantStatus = req.TenantStatus;

    await Db.UpdateNonDefaultsAsync(tenant, x => x.Id == req.Id);

    // Database is not updated.
}

When req.TenantStatus is set to TenantStatus.Active, the Db.UpdateNonDefaultsAsync method does not update the database, which seems to treat TenantStatus.Active as a default value.

Is this the intended behavior?

For the time being I’ve added a Enum TenantStatus.Undefined as the first value.

Thanks

PS. I’m using Postgresql.

I’ve added a test showing that this working as expected:

using var db = await OpenDbConnectionAsync();
db.DropAndCreateTable<DefaultValue>();

var orig = new DefaultValue {
    Id = 1,
    //...
    Status = Status.Active,
    NStatus = null,
};
await db.InsertAsync(orig);
var row = await db.SingleByIdAsync<DefaultValue>(1);
var updated = new DefaultValue
{
    Status = Status.Unknown,   // Default = Status.Unknown 
    NStatus = Status.Unknown,  // Default = null
};
OrmLiteUtils.PrintSql();
await db.UpdateNonDefaultsAsync(updated, x => x.Id == orig.Id);

row = await db.SingleByIdAsync<DefaultValue>(1);

Assert.That(row.Status, Is.EqualTo(Status.Active)); // Does not update default value
Assert.That(row.NStatus, Is.EqualTo(Status.Unknown)); // Did update non-default value

Models:

public enum Status
{
    Unknown,
    Active,
    Archived,
    Disabled,
}

public class DefaultValue
{
    public int Id { get; set; }
    //...
    public Status Status { get; set; }
    public Status? NStatus { get; set; }
}

Which for PostgreSQL prints out the expected SQL where it considers the default value of an enum as a default value for the Status property and a non-default value for the nullable Status? property which it updates:

SQL: UPDATE "default_value" SET "n_status"=:NStatus WHERE ("id" = :0)
PARAMS: :0=1, :NStatus=Unknown
SQL: SELECT "id", "int", "n_int", "bool", "n_bool", "string", "status", "n_status" FROM "default_value" WHERE "id" = :Id
PARAMS: Id=1

I’m on version 8.0 is there any chance this behavior has changed in the subsequent releases?

I’ll update and test again in the meantime.