将新实体添加到数据库时上下文未填充外键
Context not populating foreign key on adding new entities to database
我有一个问题,我从现有的 MySQL 数据库搭建了一个上下文和模型,该数据库包含一个父对多个子关系。当我尝试创建新的父项和子项时,EF-core returns 出现违反外键关系的错误。
我看了一下 EF 是如何构建它的,但没有发现任何问题。出于某种原因,针对数据库执行的代码忽略了数据模型期望填充外键这一事实。 我错过了什么会导致这种关系被忽略?
以下是相关位:
- Table 定义
- 自动脚手架上下文
- 执行保存的代码
- SQL EF 生成的引发错误的代码
这是两个表格。注意 scan 上的外键,它是 item.
的子项
项目
CREATE TABLE `item` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(250) NOT NULL COLLATE 'utf8_general_ci',
`UPC` VARCHAR(20) NOT NULL COLLATE 'utf8_general_ci',
`friendly_name` VARCHAR(250) NULL DEFAULT NULL COLLATE 'utf8_general_ci',
`date_created` DATETIME NOT NULL DEFAULT current_timestamp(),
`date_deleted` DATETIME NULL DEFAULT NULL,
`is_homemade` TINYINT(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=287
;
扫描
CREATE TABLE `scan` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`item_id` INT(11) NOT NULL DEFAULT -1,
`expiration_date` DATETIME NULL DEFAULT NULL,
`use_date` DATETIME NULL DEFAULT NULL,
`create_date` DATETIME NOT NULL DEFAULT current_timestamp(),
`delete_date` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `fk_item_instance_item` (`item_id`) USING BTREE,
CONSTRAINT `fk_item_instance_item` FOREIGN KEY (`item_id`) REFERENCES `inventory`.`item` (`id`) ON UPDATE CASCADE ON DELETE RESTRICT
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=537
;
上下文中用于映射外键的脚手架代码。这出现在模型构建器的扫描端。
entity.HasOne(d => d.Item)
.WithMany(p => p.Scan)
.HasForeignKey(d => d.ItemId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("fk_item_instance_item");
当我执行以下代码来创建一个新项目和关联的新扫描时,我从数据库中收到一个错误。
var newItem = new Item()
{
Name = scan.Name,
Upc = scan.UPC,
DateCreated = DateTime.Now,
};
var newScan = new Scan()
{
Item = newItem,
CreateDate = DateTime.Now,
ExpirationDate = scan.ExpirationDate,
};
newItem.Scan = new List<Scan>() { newScan };
if (ModelState.IsValid)
{
_context.Item.Add(newItem);
_context.Entry(newScan).State = EntityState.Added;
await _context.SaveChangesAsync();
...
数据库抛出一个错误,抱怨外键是如何被违反的。查看创建的 SQL,我可以肯定地看到它没有将项目映射到扫描。
INSERT INTO `item` (`date_created`, `date_deleted`, `friendly_name`, `is_homemade`, `name`, `UPC`)
VALUES (timestamp('2020-09-08 10:09:38.033399'), NULL, NULL, false, 'test item', 'testing');
SELECT `id`
FROM `item`
WHERE ROW_COUNT() = 1 AND `id` = LAST_INSERT_ID()
INSERT INTO `scan` (`create_date`, `delete_date`, `expiration_date`, `use_date`)
VALUES (timestamp('2020-09-08 10:09:38.037147'), NULL, NULL, NULL);
SELECT `id`, `item_id`
FROM `scan`
WHERE ROW_COUNT() = 1 AND `id` = LAST_INSERT_ID()
我认为问题在于您在保存 newItem
之前在 newScan
中使用了您的 newItem
作为参考。因此 newScan
没有获得实际的数据库实体 ID 来填充,只有需要先保存的 in-memory 副本。
这样试试-(PS:我还没有测试过这个,只是写下我认为可以解决的问题)
if (ModelState.IsValid)
{
var newItem = new Item()
{
Name = scan.Name,
Upc = scan.UPC,
DateCreated = DateTime.Now,
};
_context.Item.Add(newItem);
await _context.SaveChangesAsync();
var newScan = new Scan()
{
Item = newItem, // after EF has saved the Item entity, it should retrieve the DB generated Item automatically
CreateDate = DateTime.Now,
ExpirationDate = scan.ExpirationDate,
};
newItem.Scan = new List<Scan>() { newScan };
_context.Entry(newItem).State = EntityState.Modified;
_context.Entry(newScan).State = EntityState.Added; // Maybe you should also save this before updating the newItem a second time. It may throw an error if not!
await _context.SaveChangesAsync();
}
我有一个问题,我从现有的 MySQL 数据库搭建了一个上下文和模型,该数据库包含一个父对多个子关系。当我尝试创建新的父项和子项时,EF-core returns 出现违反外键关系的错误。
我看了一下 EF 是如何构建它的,但没有发现任何问题。出于某种原因,针对数据库执行的代码忽略了数据模型期望填充外键这一事实。 我错过了什么会导致这种关系被忽略?
以下是相关位:
- Table 定义
- 自动脚手架上下文
- 执行保存的代码
- SQL EF 生成的引发错误的代码
这是两个表格。注意 scan 上的外键,它是 item.
的子项项目
CREATE TABLE `item` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`name` VARCHAR(250) NOT NULL COLLATE 'utf8_general_ci',
`UPC` VARCHAR(20) NOT NULL COLLATE 'utf8_general_ci',
`friendly_name` VARCHAR(250) NULL DEFAULT NULL COLLATE 'utf8_general_ci',
`date_created` DATETIME NOT NULL DEFAULT current_timestamp(),
`date_deleted` DATETIME NULL DEFAULT NULL,
`is_homemade` TINYINT(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`) USING BTREE
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=287
;
扫描
CREATE TABLE `scan` (
`id` INT(11) NOT NULL AUTO_INCREMENT,
`item_id` INT(11) NOT NULL DEFAULT -1,
`expiration_date` DATETIME NULL DEFAULT NULL,
`use_date` DATETIME NULL DEFAULT NULL,
`create_date` DATETIME NOT NULL DEFAULT current_timestamp(),
`delete_date` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE,
INDEX `fk_item_instance_item` (`item_id`) USING BTREE,
CONSTRAINT `fk_item_instance_item` FOREIGN KEY (`item_id`) REFERENCES `inventory`.`item` (`id`) ON UPDATE CASCADE ON DELETE RESTRICT
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=537
;
上下文中用于映射外键的脚手架代码。这出现在模型构建器的扫描端。
entity.HasOne(d => d.Item)
.WithMany(p => p.Scan)
.HasForeignKey(d => d.ItemId)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("fk_item_instance_item");
当我执行以下代码来创建一个新项目和关联的新扫描时,我从数据库中收到一个错误。
var newItem = new Item()
{
Name = scan.Name,
Upc = scan.UPC,
DateCreated = DateTime.Now,
};
var newScan = new Scan()
{
Item = newItem,
CreateDate = DateTime.Now,
ExpirationDate = scan.ExpirationDate,
};
newItem.Scan = new List<Scan>() { newScan };
if (ModelState.IsValid)
{
_context.Item.Add(newItem);
_context.Entry(newScan).State = EntityState.Added;
await _context.SaveChangesAsync();
...
数据库抛出一个错误,抱怨外键是如何被违反的。查看创建的 SQL,我可以肯定地看到它没有将项目映射到扫描。
INSERT INTO `item` (`date_created`, `date_deleted`, `friendly_name`, `is_homemade`, `name`, `UPC`)
VALUES (timestamp('2020-09-08 10:09:38.033399'), NULL, NULL, false, 'test item', 'testing');
SELECT `id`
FROM `item`
WHERE ROW_COUNT() = 1 AND `id` = LAST_INSERT_ID()
INSERT INTO `scan` (`create_date`, `delete_date`, `expiration_date`, `use_date`)
VALUES (timestamp('2020-09-08 10:09:38.037147'), NULL, NULL, NULL);
SELECT `id`, `item_id`
FROM `scan`
WHERE ROW_COUNT() = 1 AND `id` = LAST_INSERT_ID()
我认为问题在于您在保存 newItem
之前在 newScan
中使用了您的 newItem
作为参考。因此 newScan
没有获得实际的数据库实体 ID 来填充,只有需要先保存的 in-memory 副本。
这样试试-(PS:我还没有测试过这个,只是写下我认为可以解决的问题)
if (ModelState.IsValid)
{
var newItem = new Item()
{
Name = scan.Name,
Upc = scan.UPC,
DateCreated = DateTime.Now,
};
_context.Item.Add(newItem);
await _context.SaveChangesAsync();
var newScan = new Scan()
{
Item = newItem, // after EF has saved the Item entity, it should retrieve the DB generated Item automatically
CreateDate = DateTime.Now,
ExpirationDate = scan.ExpirationDate,
};
newItem.Scan = new List<Scan>() { newScan };
_context.Entry(newItem).State = EntityState.Modified;
_context.Entry(newScan).State = EntityState.Added; // Maybe you should also save this before updating the newItem a second time. It may throw an error if not!
await _context.SaveChangesAsync();
}