【问题标题】:Owned types collection is never updated in EF Core拥有的类型集合永远不会在 EF Core 中更新
【发布时间】:2020-06-03 17:06:00
【问题描述】:

我有一个这样定义的聚合:

public class Product {
    public int LocalId { get; private set; }
    public Something Something { get; private set; }
    public ICollection<Price> Prices { get; private set; }
}

public class Something {
    public string Name { get; set; }
}

public class Price {
    public int Type { get; set; }
    public decimal Value { get; set; }
}

还有这样定义的架构:

private void DefineProduct(ModelBuilder builder) =>
    builder
        .Entity<Product>(builder =>
        {
            builder.HasKey(p => p.LocalId);
            builder
                .OwnsOne(p => p.Something, smth =>
                {
                    smth.ToTable("somethings");
                })
                .OwnsMany(p => p.Prices, pp =>
                {
                    pp.ToTable("prices");
                });
        });

当请求更改价格时,我会这样做(为简洁起见,在产品方法中不包括在内):

Prices.First(p => p.Type == type).Value = newValue;

然后我尝试像这样保存产品:

public async Task UpdateProperties(Product product, IEnumerable<object> props)
{
    _context.Attach(product);
    _context.Update(product);

    foreach (var prop in props)
    {
        _context.Update(prop);
    }

    try
    {
        await _context.SaveChangesAsync();
    } 
    catch (Exception ex)
    {
        Console.WriteLine("Who the hell allowed such a bug to go into a production release?");
    }
}

现在我应该提到该产品来自未跟踪结果的初始查询(通过AsNoTracking() 调用),这就是我在方法主体的第一行调用Attach 方法的原因。问题是我在使用catch 语句时出现异常消息:

数据库操作预计会影响 1 行,但实际上会影响 0 行。数据可能已被修改或删除,因为实体 加载。见http://go.microsoft.com/fwlink/?LinkId=527962 有关理解和处理乐观并发的信息 例外。”}

问题是我不会在其他任何地方更新相同的产品,这是唯一接触它的地方。我也使用AsNoTracking 作为默认值。如果我用_context.Update(prop); 注释掉该行,则不会引发异常,但价格不会更新。此外,如果我不更新该价格集合而是更新Something 属性,那么一切顺利。什么。这。地狱。

【问题讨论】:

  • 嗨,不完全确定:_context.Attach(product).State = EntityState.Modified; 向 EF 发出信号,表示它应该重新跟踪它。 (对于 EF 非核心:_context.Entry(product).State = EntityState.Modified;)
  • 我遇到了同样的错误:(

标签: c# entity-framework-core owned-types


【解决方案1】:

Collections of owned types 的 EF Core 文档明确指出您必须定义拥有的实体 PK(与通常将影子 FK 用作 PK 的OwnsOne 不同)。

因此您需要定义自己的 PK(如您所做的 Id)或复合 PK - 例如,如果 Price.Type 在所有者内部是唯一的,那么您可以使用类似

pp.HasKey("LocalId", "Type");

并避免使用额外的Id 列。

【讨论】:

  • 问题是EF框架已经在数据库中创建了主键。它有点反对您提供的链接中的那句话,即拥有的类型需要一个主键。既然它是自动创建的,为什么我需要在我的模式定义中明确提及它?它应该要么都是明确的,但有例外对我大喊我不应该在没有明确命名主键的情况下定义拥有的集合类型,或者 100% 隐式,所以自动创建的 pk 应该足够了。现在它只是令人困惑......
  • 同样获得 DbUpdateConcurrencyException 类型的异常无助于调试问题:(
  • 您写道“当我在 Price 对象上添加 Id 字段时,我的代码开始正常运行”。所以在此之前它不是自动为你创建的。无论如何,链接是官方的 EF Core 文档,所以如果它是最新的,那么它应该是它所说的。顺便说一句,您还没有指定 EF Core 版本,这也可能很重要,因为它们甚至在次要版本之间更改/破坏了许多东西。异常呢,它们基本上用于每个可能的错误 - 例如尝试删除或更新不存在的记录。所以,是的,该消息具有误导性:-(
  • 好的,然后我错过了一个重要的信息——db 表有一个名为 id 的主键列,它是由 orm 创建的。无论如何,谢谢你的解释!
【解决方案2】:

我正在为未来迷路的旅行者发布这个答案,尽管我很乐意听到比我更了解 EF Core 的人的解释。我也认为我理解这种行为的原因,但我不确定。当我在 Price 对象上添加 Id 字段时,我的代码开始正常运行。我的怀疑是,如果没有明确可见的 id 属性,任何附加或更新都不会让 EF 看到该对象。不过,我会欢迎有关它的文档部分...

【讨论】:

  • 虽然我无法快速找到说明当您不提供密钥时会发生什么的文档,但这里有关于 Keys 的文档。按照惯例,您在添加 Price.Id 时添加了 Key。
猜你喜欢
  • 2021-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-04-03
相关资源
最近更新 更多