【问题标题】:Entity Framework - New child entities not added to database on saving parent entity (only in production environment)实体框架 - 保存父实体时未将新子实体添加到数据库(仅在生产环境中)
【发布时间】:2018-05-31 10:42:33
【问题描述】:

我在应用程序中遇到了一个奇怪的问题。我有以下代码更新父实体并添加新的子实体。

    Item item = db.Items.Find(Id);

    if (newSubItems.Count() > 0)
    {
        newSubItems.ForEach(x =>
        {
            var subItem = new SubItem();
            subItem.Name = x.Name;

            item.SubItems.Add(subItem);
        });
    }

    item.ModifiedAt = DateTime.Now;
    item.ModifiedBy = UserId;

    db.Entry(item).State = EntityState.Modified;

    using (var s = db.Database.BeginTransaction(isolationLevel: IsolationLevel.RepeatableRead))
    {
        await db.SaveChangesAsync();
        s.Commit();

        logger.info("Updated successfully.");
    }

此代码在我的本地环境中运行良好。如果我提供新的子项,这些子项将成功添加到相应的表中。

型号如下。

    public partial class Item
    {
        public Item()
        {
            this.SubItems = new HashSet<SubItem>();
        }

        public int Id { get; set; }
        public DateTime ModifiedAt { get; set; }
        public int ModifiedBy { get; set; }
        public virtual ICollection<SubItem> SubItems { get; set; }
    }

    public partial class SubItem
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int ItemId { get; set; }
        public Item Item { get; set; }
    }

但是,这在我的生产环境中无法正常工作。父实体已更新,但如果没有现有的子实体,则不会添加新的子实体。我检查了日志,我可以看到记录了“更新成功”。如果父实体至少有 1 个子实体,则成功添加新的子实体。

所以现在作为生产环境中的一种解决方法,我在使用以下代码进行第一次保存操作后再次重新添加子项。

    int subItemsCount = db.SubItems.Where(a => a.ItemId == item.Id).Count();

    if (subItemsCount == 0 && newSubItems.Count() > 0)
    {
        logger.info(string.Format("Sub-items are not added for Id - {0}. Adding those again.", item.Id));

        newSubItems.ForEach(x =>
        {
            var subItem = new SubItem();
            subItem.Name = x.Name;
            subItem.ItemId = item.Id;

            db.SubItems.Add(subItem);
        });

        await db.SaveChangesAsync();

        logger.info(string.Format("Sub-items re-added successfully for Id - {0}.", item.Id));
    }

现在查看生产环境的日志,我可以看到“没有为 Id 添加子项”的消息被记录了很多次,并且在第二次保存操作中成功添加了子项。

想知道是否有人仅在特定环境中知道这种奇怪行为的原因。

【问题讨论】:

  • 在获取项目详细信息时尝试包含子项目(使用 .Include)Item item = db.Items.Find(Id); 使用 Item item = db.Items.Include(i =&gt; i.SubItems).Where(i =&gt; i.Id == Id);
  • 另外,您可能不需要db.Entry(item).State = EntityState.Modified;,因为您未处于分离模式(除非您已明确禁用更改跟踪)
  • @Developer 感谢您的建议。我相信,默认情况下,应该加载所有子实体并且需要 .Include,前提是我手动禁用了 LazyLoadingEnabled 属性。我将尝试按照您的建议删除将状态更新为 modified 的第二条语句,但我仍然不确定该语句是否会导致这样的问题。
  • 能否请您检查生产数据库中的外键关系是否设置正确?同样在您的 SubItem 实体中,缺少 Item 的导航属性以使 EF 了解 Item 外键 - 不确定您是否使用流畅的语法设置它
  • @Developer FK 关系在生产中是正确的。我什至尝试在我的开发环境中使用生产数据库副本,它工作正常。在此处添加示例模型时,我错过了导航属性。现在更正它。

标签: c# sql-server entity-framework linq


【解决方案1】:

在您的第一种方法中,您应该在对其执行item.SubItems.Add() 之前检查item.SubItems 是否为空。

如果为空,则像item.SubItems = new ICollection&lt;SubItem&gt;();一样初始化

在您的第二种方法中,在此代码块中您没有分配 ItemId

 newSubItems.ForEach(x =>
        {
            var subItem = new SubItem();
            subItem.Name = x.Name;
            subItem.ItemId = item.Id;/* missing line*/
            db.SubItems.Add(subItem);
        });

【讨论】:

  • 感谢您的回复。我的第二种方法对我来说效果很好。你说得对,我错过了一行subItem.ItemId = item.Id;。我已经更新了这个问题。现在来到第一种方法。如果像你说的那样值为空,当我尝试使用db.SubItems.Add(subItem);向它添加项目时,它不应该抛出NullReferenceException吗?
  • @Manu 我想知道在您的 SubItem 类中的三个属性中,您只分配了名称。原始代码中的 ItemId 属性呢?
  • Id 是一个自动递增的标识元素。而且我相信ItemId 不需要明确指定,因为我正在将子项目对象添加到项目对象的子集合中。我想这种关系应该由实体框架自动解决。最重要的是,这段代码在我的本地环境中运行良好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-31
  • 1970-01-01
相关资源
最近更新 更多