我有一段数据库模式,它对于 SQL Server 来说运行得非常好:
builder.Property(b => b.FieldName)
.UseIdentityColumn(1, 1)
.ValueGeneratedOnAdd();
但是当我尝试切换到 SQLite 进行测试时,我得到了错误:
SQLite Error 19: 'NOT NULL constraint failed: TableName.FieldName'
。
根据我的理解,
ValueGeneratedOnAdd
在SQLite中不适用于非PK字段(在SQL Server中它会自动递增)。
有什么解决方法可以解决这个问题吗?
注意此答案不提供 EntityFramework 解决方案,而是尝试提供 SQLite 如何处理自动生成标识特定行的唯一值的背景。
这个答案可能有用如何使用 EF core 在 SQLite 中创建自动增量列?
有什么解决方法可以解决这个问题吗? 是的,从理论上讲,可能与模式无关!
SQLite 非常灵活,因此与其他数据库有很大不同。
可能什么也不做的基础
除了虚拟表和WITHOUT ROWID 表之外的所有表的SQLite 都有一列是唯一生成的整数值。这是 rowid 列。该列是隐藏列,因此需要显式引用。
但是@#$% 自动增量?
SQLite 致力于能够使用 SQL 从其他数据库创建模式。已利用 AUTOINCRMENT 关键字/约束来更接近地模仿 AUTOINCRMENT 的有意使用,尽管歪曲了其用途。
当使用 AUTOINCRMENT 时,对于定义为 INTEGER PRIMARY KEY 的列,它必须是。 (每个表只能存在 1 个这样的列)。使用时,约束/规则是,如果自动生成的值“必须”大于任何现有或曾经使用过的值。即保证价值会增加。
列的别名。
并跳过 .ValueGeneratedOnAdd();
但是,当需要该值时,通过指定 rowid (或 oid 或 rowid)来提取它 . 或者,您可以指定一个整数列作为主键并使其可为空(如果可能的话,即您可能不希望 NOT NULL,因为包装器在插入时倾向于使用 null)。
这里有一个演示,展示了 SQLite 生成的一些工作原理:-
DROP TABLE IF EXISTS a;
CREATE TABLE IF NOT EXISTS a (c1);
/* Insert 2 rows where the value in column c1 has nothing at all to do with the rowid */
INSERT INTO a VALUES('anything');
INSERT INTO a VALUES('yet another anything');
/* Now change the rowid(s) to 100 times what they were */
UPDATE a SET rowid = rowid * 100;
/* now insert 10 rows via recursive CTE */
WITH
cte1(i) AS (SELECT 1 UNION ALL SELECT cte1.i+1 FROM cte1 WHERE i < 10 LIMIT 10)
INSERT INTO a SELECT 'xxx'||(i*100) FROM cte1;
/* play around with the maximum possible rowid */
/* add 2 rows one at max the other with generated value NOTE!! no AUTOINCREMENT */
INSERT INTO a (rowid,c1) VALUES
(9223372036854775807,'maxrowid'), /* specify the MAX possible value for this row */
(null,'higher then max rowid?????'); /* null!!!!???? SQLite allows null and then generates the value */
/* add another row with generated rowid */
INSERT INTO a (c1) VALUES('another higher than max rowid!!!!!');
/* Output as per typicalll all columns query */
SELECT * FROM a;
/* now add the hidden rowid column to the output */
SELECT *,rowid, oid, _rowid_ FROM a;
/*<<<<<<<<<< TABLE B ROWID ALIAS >>>>>>>>>>*/
/* Add table B now with a column rowid_alias as an alias of the rowid column */
DROP TABLE IF EXISTS b;
CREATE TABLE IF NOT EXISTS b (rowid_alias INTEGER PRIMARY KEY, c1);
/* Populate table b from column c1 only of table a (i.e. rowid and thus alias generated) */
INSERT INTO b (c1) SELECT c1 FROM a;
/* Now show both table a and table b JOINED BY the c1 column */
SELECT b.*, b.rowid AS rowid_from_b, a.*, a.rowid AS rowid_from_a FROM b JOIN a ON b.c1 = a.c1;
/*<<<<<<<<<< TABLE C - AUTOINCREMENT >>>>>>>>>>*/
/* Show the schema for prior to table c */
DROP TABLE IF EXISTS c;
CREATE TABLE IF NOT EXISTS c (rowid_alias INTEGER PRIMARY KEY AUTOINCREMENT, c1);
INSERT INTO c SELECT rowid,* FROM a UNION SELECT * FROM b;
SELECT * FROM c;
SELECT * FROM sqlite_sequence;
INSERT INTO b (c1) VALUES('rowid still has max');
/* NOTE COMMENT OUT THE NEXT LINE IF ENVIRONMENT IS TO BE CLEANED */
INSERT INTO c (c1) VALUES('what about AUTOINCREMENT when max rowid has been used');
DELETE FROM sqlite_sequence;
DROP TABLE IF EXISTS a;
DROP TABLE IF EXISTS b;
DROP TABLE IF EXISTS c;
运行时(故意包含 SQLITE_FULL 失败),会产生以下输出:-结果 1
表中的典型摘录 a:-
即没什么特别的表 a 以及隐藏的 rowid 列:-
即通过对标准表不执行任何操作,可以通过 rowid、oid 或表 b 具有 INTEGER PRIMARY KEY,因此是 rowid 列和通过 c1 列连接的表 a 的别名。
结果 4表 c 具有自动增量功能,其中加载了表 a 和表 c 中的行。注意没有生成值。 :-
结果 5自动增量使用的 sqlite_sequence :-
消息/错误由于在使用了可能的最高生成值时尝试插入表c:-
/* NOTE COMMENT OUT THE NEXT LINE IF ENVIRONMENT IS TO BE CLEANED */
INSERT INTO c (c1) VALUES('what about AUTOINCREMENT when max rowid has been used')
> database or disk is full
> Time: 0s
最后一个警告,不使用 rowid(整数主键)的别名可能会导致以下问题:-