dotnet / efcore

EF Core is a modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
https://docs.microsoft.com/ef/
MIT License
13.77k stars 3.18k forks source link

SQLite - Can't insert null into TIMESTAMP column #20475

Open Swellenator opened 4 years ago

Swellenator commented 4 years ago

Using in memory EF Core SQLite for integration tests. When creating an entity with the following column definition:

              entity.Property(e => e.TxTimestamp)
                    .IsRequired()
                    .IsRowVersion()
                    .IsConcurrencyToken();

Setting with

TxTimestamp = new byte[8] { 0, 1, 2, 3, 4, 5, 6, 7 }

I get the following error:

Microsoft.Data.Sqlite.SqliteException : SQLite Error 19: 'NOT NULL constraint failed: [TableName].TxTimestamp'.

Any ideas? It is like SQLite isn't inserting the value. Perhaps because with real SQL Server it wouldn't be able to? This model is generated from a SQL Server database and we are using database first, so I can't just change the model builder definition. (unless there is an easy way to override the one generated form the scaffold).

Additional context

Microsoft.Data.Sqlite version: 3.1.3 Target framework: netcoreapp3.1 Operating system: Win 10

ajcvickers commented 4 years ago

Notes for team: In this case the property is configured for automatic concurrency with an expectation that this will work like it does in SQL Server. This is tracked by #2195. Within this, the insert of null happens because the SQLite provider doesn't support computed properties--tracked by #19682.

Should we check for one or both of these patterns in the model validator?

Model: 
  EntityType: Customer
    Properties: 
      Id (int) Required PK AfterSave:Throw ValueGenerated.OnAdd
      TxTimestamp (byte[]) Required Concurrency BeforeSave:Ignore AfterSave:Ignore ValueGenerated.OnAddOrUpdate
    Keys: 
      Id PK

/cc @bricelam

Swellenator commented 4 years ago

My work around is:

 var sql = context.Database.GenerateCreateScript();

 // HACK: ef core wont put data into a rowversion column, but it is not null, so create a fake default
 sql = sql.Replace("\"TxTimestamp\" BLOB NOT NULL", "\"TxTimestamp\" BLOB NOT NULL DEFAULT (randomblob(8))");         
 await context.Database.ExecuteSqlRawAsync(sql);

to create a default value for the column. Certainly not ideal

bricelam commented 4 years ago

See also https://github.com/dotnet/efcore/issues/12260#issuecomment-395509124

ajcvickers commented 4 years ago

@bricelam I couldn't find an issue about comments/warnings in the generated code.

bricelam commented 4 years ago

Found it: https://github.com/dotnet/efcore/issues/2461

bricelam commented 4 years ago

This exact scenario was previously discussed in https://github.com/dotnet/efcore/issues/7295

ajcvickers commented 4 years ago

Putting this on the backlog to consider generating some form of inline warning.

foreverhot1019 commented 1 year ago

Use [ConcurrencyCheck] to Manual management ConcurrencyCheck Key。 Sqlite not support [Timestamp] automaticlly(Timestamp Include RowVersion & ConcurrencyCheck)

dorssel commented 8 months ago

FYI, I created https://github.com/dorssel/dotnet-ef-sqlite-timestamp