【问题标题】:Which is better for database seeding: Add or AddOrUpdate?哪个更适合数据库播种:Add 还是 AddOrUpdate?
【发布时间】:2015-01-13 17:18:55
【问题描述】:

我不明白为什么到处都建议在 Seed 方法中使用 AddOrUpdate?

我们已经开发了半年的应用程序,每次更新服务器时,AddOrUpdates 都会覆盖用户更改。例如。如果我们调用种子:

context.Styles.AddOrUpdate(new Style { Id = 1,  Color = "red"  });

用户将样式更改为“绿色”,然后在下一次服务器更新时,我们再次将其覆盖为“红色”,这让用户非常恼火。

看起来,如果我们将 AddOrUpdate 更改为 Add,我们将保证不会覆盖用户数据。如果我们仍然需要一些特殊情况,我们可以将其单独迁移。与一般的 Configuration.Seed 方法不同,特定的迁移不会在同一个数据库版本上运行两次。

【问题讨论】:

  • 看起来您只需要坐下来使用数据库和 EF,并弄清楚如何正确进行迁移,以免发生数据丢失。
  • @RobertHarvey,这就是我正在尝试做的事情,到目前为止还没有成功。添加解释。
  • 请不要更改问题的标题以改变其含义。关于这个问题,我已经在网上搜索了很多天。有很多各种各样的信息。但正是缺少这条信息。我对自己能找到的其他东西不感兴趣。
  • 不建议到处使用:thedatafarm.com/data-access/…
  • @Colin,谢谢你的链接。它帮助我不再像以前那样孤独J

标签: entity-framework entity-framework-migrations


【解决方案1】:

我假设Style 的主键是Id。您使用的The overload of AddOrUpdate 仅检查是否存在具有Id == 1 的记录。如果是这样,它会更新它。就是这样。

这里的问题是主键是代理键,即它是为了方便查询而存在的,但它没有任何商业意义。通常,通过迁移,您希望寻找实体的自然键。这就是用户识别数据的方式。他/她想要的是绿色风格,而不是 1 标识的风格。

所以我认为你应该使用this overload of AddOrUpdate:

context.Styles.AddOrUpdate( s => s.Color,
                            new Style { Id = 1,  Color = "red"  });

现在当不再有红色样式时,插入一个新样式,覆盖Id 值(假设它是由数据库生成的)。

从您以后的 cmets 中,我了解到您希望在新数据时添加数据,但在它们存在时不更新它们(与主键相比)。为此,您可以使用我描述的hereAddWhenNew 方法的略微修改版本。对于您的情况,我会这样做:

public T void MarkAsAddedWhenNew<T>(this DbContext context, 
        Expression<Func<T, object>> identifierExpression, T item) 
    where T : class
{
    context.Set<T>().AddOrUpdate(identifierExpression, item);
    if (context.Entry(item).State != System.Data.Entity.EntityState.Added)
    {
        var identifierFunction = identifierExpression.Compile();
        item = context.Set<T>()
               .Local
               .Single(x => identifierFunction(item)
                            .Equals(identifierFunction(x)));
        context.Entry(item).State = System.Data.Entity.EntityState.Unchanged;
    }
    return item;
}

从本地集合中重新获取项目是一件麻烦事,但由于AddOrUpdate() 中的bug 是必要的。此错误还导致您在将原始条目的状态设置为 Unchanged 时遇到错误:它与附加的实例不同。

【讨论】:

  • 我觉得这里的主键没有错。 Style 对象可以连接到 User 对象,User 到 Friends 等。它们都通过 id 连接。是的,在完美世界中我们应该使用自然键,但实际上使用主键是最好的实用方法。如果缺少红色样式,我不需要插入它。
  • 对不起。罗伯特更改了问题的标题。我不知道为什么。我已将问题的标题恢复为初始值。
  • 我尝试使用您的 AddWhenNew 方法,但不幸的是它会引发异常。请参阅我的答案中的注释代码。
  • 将状态更改为Unchanged 就像附加实体两次。
  • 我想这就是你想要的。
【解决方案2】:

Add 方法的行为方式具有误导性。即使已经有一行与我们添加的 PrimaryKey 相同,它也会将数据插入数据库。它只是默默地创建了新的 PrimaryKey 忽略了我们的值。我应该在问这个问题之前尝试一下,但无论如何,我想我不是唯一一个对此感到困惑的人。所以,在我的情况下,Add 甚至比 AddOrUpdate 更糟糕。

我找到的唯一解决方案如下:

    public static void AddWhenNew<T>(this DbContext ctx, T item) where T : Entity
    {
        var old = ctx.Set<T>().Find(item.Id);
        if (old == null) 
            ctx.Set<T>().AddOrUpdate(item);

        /* Unfortunately this approach throws exception when I try to set state to Unchanged.
        Something like:"The entity already exists in the context"
        ctx.Set<T>().AddOrUpdate(item);
        if (ctx.Entry(item).State != System.Data.Entity.EntityState.Added)            
            ctx.Entry(item).State = System.Data.Entity.EntityState.Unchanged;              
        */
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-15
    • 2022-01-13
    • 2011-10-23
    • 1970-01-01
    • 2012-10-23
    • 2021-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多