【问题标题】:Context not populating foreign key on adding new entities to database在向数据库添加新实体时上下文不填充外键
【发布时间】:2020-12-26 23:27:37
【问题描述】:

我有一个问题,我从现有的 MySQL 数据库中搭建了一个上下文和模型,该数据库包含一个父级到多个子级的关系。当我尝试创建新的父级和子级时,EF-core 返回错误外键关系已被违反。

我查看了 EF 是如何搭建它的,但看不到任何问题。出于某种原因,针对数据库执行的代码忽略了数据模型期望填充外键这一事实。 我错过了什么会导致这种关系被忽略?

以下是相关位:

  1. 表定义
  2. 自动脚手架上下文
  3. 执行保存的代码
  4. EF 生成的引发错误的 SQL 代码

这是两张表。注意 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()

【问题讨论】:

    标签: c# mysql asp.net-mvc entity-framework-core entity-framework-core-3.1


    【解决方案1】:

    我认为问题在于您在保存newItem 之前使用newItem 作为newScan 中的引用。所以newScan 没有得到要填充的实际数据库实体 ID,只有需要先保存的内存中副本。

    这样试试——(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();    
        
    }
    

    【讨论】:

    • 这种方法的变体有效。我不必将项目标记为已修改,只需添加扫描。我可以明白为什么这会起作用,但它与关于saving related entities 的微软文档不符。在那里,您可以看到博客和帖子是在同一批次中创建的。 EF 核心与框架有很大不同,我正在努力解决这样的问题 -_-;
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-08-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多